Skip to content

Commit

Permalink
Merge pull request #4 from neuralnorthwest/develop
Browse files Browse the repository at this point in the history
Release v0.0.4
  • Loading branch information
smxlong committed Mar 1, 2023
2 parents 99e9914 + fd62231 commit c3c7070
Show file tree
Hide file tree
Showing 4 changed files with 333 additions and 4 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## v0.0.4

### Added

* Added `PreHook` and `PostHook` to `git.Repository` to allow interception of
git operations.

## v0.0.3

### Fixed
Expand Down
46 changes: 43 additions & 3 deletions git/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ type Repository struct {
MainBranch string
// Dir is the path to the directory where the Git repository is cloned.
Dir string
// PreHook is a function that is called before a Git command is executed.
PreHook func(*exec.Cmd) error
// PostHook is a function that is called after a Git command is executed.
PostHook func(*exec.Cmd, error) error
// fs is the filesystem interface.
fs
}
Expand Down Expand Up @@ -102,10 +106,24 @@ func (r *Repository) Clone(args ...string) error {
if err := r.fs.MkdirAll(filepath.Dir(r.Dir), 0755); err != nil {
return err
}
var cmd *exec.Cmd
if r.MainBranch == "" {
return exec.Command("git", append(append([]string{"clone"}, args...), r.URL, r.Dir)...).Run()
cmd = exec.Command("git", append(append([]string{"clone"}, args...), r.URL, r.Dir)...)
} else {
cmd = exec.Command("git", append(append([]string{"clone", "-b", r.MainBranch}, args...), r.URL, r.Dir)...)
}
return exec.Command("git", append(append([]string{"clone", "-b", r.MainBranch}, args...), r.URL, r.Dir)...).Run()
if r.PreHook != nil {
if herr := r.PreHook(cmd); herr != nil {
return herr
}
}
err := cmd.Run()
if r.PostHook != nil {
if herr := r.PostHook(cmd, err); herr != nil {
return herr
}
}
return err
} else {
return fmt.Errorf("repository already cloned: %s", r.URL)
}
Expand Down Expand Up @@ -171,13 +189,35 @@ func (r *Repository) LockerPID() (int, error) {
// Exec executes a command in the Git repository.
func (r *Repository) Exec(args ...string) error {
args = append([]string{"-C", r.Dir}, args...)
return exec.Command("git", args...).Run()
cmd := exec.Command("git", args...)
if r.PreHook != nil {
if herr := r.PreHook(cmd); herr != nil {
return herr
}
}
err := cmd.Run()
if r.PostHook != nil {
if herr := r.PostHook(cmd, err); herr != nil {
return herr
}
}
return err
}

// ExecOutput executes a command in the Git repository and returns its output.
func (r *Repository) ExecOutput(args ...string) (string, error) {
args = append([]string{"-C", r.Dir}, args...)
cmd := exec.Command("git", args...)
if r.PreHook != nil {
if herr := r.PreHook(cmd); herr != nil {
return "", herr
}
}
out, err := cmd.Output()
if r.PostHook != nil {
if herr := r.PostHook(cmd, err); herr != nil {
return "", herr
}
}
return string(out), err
}
282 changes: 282 additions & 0 deletions git/git_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -326,3 +326,285 @@ func Test_GitRepository_LockerPID(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, os.Getpid(), pid)
}

// Test_GitRepository_LockFile_Write_Failed tests the LockFile function when
// writing the lock file fails.
func Test_GitRepository_LockFile_Write_Failed(t *testing.T) {
t.Parallel()
mockCtrl := gomock.NewController(t)
mockfs := mock_git.NewMockfs(mockCtrl)
mockfs.EXPECT().Stat(gomock.Any()).DoAndReturn(os.Stat)
mockfs.EXPECT().MkdirAll(gomock.Any(), gomock.Any()).DoAndReturn(os.MkdirAll)
mockfs.EXPECT().OpenFile(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil) // return a nil file
c, _, cleanup := setupCache(t)
defer cleanup()
c.fs = mockfs
repo, cleanup := newLocalTestRepo(t, c)
defer cleanup()
assert.NoError(t, repo.Clone())
_, err := repo.Lock()
assert.Error(t, err)
}

// Test_GitRepository_Hooks_Clone tests the Hooks function when cloning.
func Test_GitRepository_Hooks_Clone(t *testing.T) {
t.Parallel()
c, _, cleanup := setupCache(t)
defer cleanup()
repo, cleanup := newLocalTestRepo(t, c)
defer cleanup()
preHookCalled := new(int)
postHookCalled := new(int)
n := 0
repo.PreHook = func(*exec.Cmd) error {
*preHookCalled = n
n++
return nil
}
repo.PostHook = func(*exec.Cmd, error) error {
*postHookCalled = n
n++
return nil
}

assert.NoError(t, repo.Clone())
assert.Equal(t, 0, *preHookCalled)
assert.Equal(t, 1, *postHookCalled)
}

// Test_GitRepository_Hooks_Clone_PreFail tests the Hooks function when cloning
// and the pre hook fails.
func Test_GitRepository_Hooks_Clone_PreFail(t *testing.T) {
t.Parallel()
c, _, cleanup := setupCache(t)
defer cleanup()
repo, cleanup := newLocalTestRepo(t, c)
defer cleanup()
preHookCalled := new(int)
postHookCalled := new(int)
n := 0
repo.PreHook = func(*exec.Cmd) error {
*preHookCalled = n
n++
return errors.New("test")
}
repo.PostHook = func(*exec.Cmd, error) error {
*postHookCalled = n
n++
return nil
}

assert.ErrorContains(t, repo.Clone(), "test")
assert.Equal(t, 0, *preHookCalled)
assert.Equal(t, 0, *postHookCalled)
}

// Test_GitRepository_Hooks_Clone_PostFail tests the Hooks function when cloning
// and the post hook fails.
func Test_GitRepository_Hooks_Clone_PostFail(t *testing.T) {
t.Parallel()
c, _, cleanup := setupCache(t)
defer cleanup()
repo, cleanup := newLocalTestRepo(t, c)
defer cleanup()
preHookCalled := new(int)
postHookCalled := new(int)
n := 0
repo.PreHook = func(*exec.Cmd) error {
*preHookCalled = n
n++
return nil
}
repo.PostHook = func(*exec.Cmd, error) error {
*postHookCalled = n
n++
return errors.New("test")
}

assert.ErrorContains(t, repo.Clone(), "test")
assert.Equal(t, 0, *preHookCalled)
assert.Equal(t, 1, *postHookCalled)
}

// Test_GitRepository_Hooks_Exec tests the Hooks function when executing a
// command.
func Test_GitRepository_Hooks_Exec(t *testing.T) {
t.Parallel()
c, _, cleanup := setupCache(t)
defer cleanup()
repo, cleanup := newLocalTestRepo(t, c)
defer cleanup()
preHookCalled := new(int)
postHookCalled := new(int)
n := 0
repo.PreHook = func(*exec.Cmd) error {
*preHookCalled = n
n++
return nil
}
repo.PostHook = func(*exec.Cmd, error) error {
*postHookCalled = n
n++
return nil
}

assert.NoError(t, repo.Clone())
assert.NoError(t, repo.Exec("status"))
assert.Equal(t, 2, *preHookCalled)
assert.Equal(t, 3, *postHookCalled)
}

// Test_GitRepository_Hooks_Exec_PreFail tests the Hooks function when executing
// a command and the pre hook fails.
func Test_GitRepository_Hooks_Exec_PreFail(t *testing.T) {
t.Parallel()
c, _, cleanup := setupCache(t)
defer cleanup()
repo, cleanup := newLocalTestRepo(t, c)
defer cleanup()
preHookCalled := new(int)
postHookCalled := new(int)
n := 0
repo.PreHook = func(*exec.Cmd) error {
*preHookCalled = n
n++
if n > 1 {
return errors.New("test")
}
return nil
}
repo.PostHook = func(*exec.Cmd, error) error {
*postHookCalled = n
n++
return nil
}

assert.NoError(t, repo.Clone())
assert.ErrorContains(t, repo.Exec("status"), "test")
assert.Equal(t, 2, *preHookCalled)
assert.Equal(t, 1, *postHookCalled)
}

// Test_GitRepository_Hooks_Exec_PostFail tests the Hooks function when executing
// a command and the post hook fails.
func Test_GitRepository_Hooks_Exec_PostFail(t *testing.T) {
t.Parallel()
c, _, cleanup := setupCache(t)
defer cleanup()
repo, cleanup := newLocalTestRepo(t, c)
defer cleanup()
preHookCalled := new(int)
postHookCalled := new(int)
n := 0
repo.PreHook = func(*exec.Cmd) error {
*preHookCalled = n
n++
return nil
}
repo.PostHook = func(*exec.Cmd, error) error {
*postHookCalled = n
n++
if n > 2 {
return errors.New("test")
}
return nil
}

assert.NoError(t, repo.Clone())
assert.ErrorContains(t, repo.Exec("status"), "test")
assert.Equal(t, 2, *preHookCalled)
assert.Equal(t, 3, *postHookCalled)
}

// Test_GitRepository_Hooks_ExecOutput tests the Hooks function when executing a
// command and capturing the output.
func Test_GitRepository_Hooks_ExecOutput(t *testing.T) {
t.Parallel()
c, _, cleanup := setupCache(t)
defer cleanup()
repo, cleanup := newLocalTestRepo(t, c)
defer cleanup()
preHookCalled := new(int)
postHookCalled := new(int)
n := 0
repo.PreHook = func(*exec.Cmd) error {
*preHookCalled = n
n++
return nil
}
repo.PostHook = func(*exec.Cmd, error) error {
*postHookCalled = n
n++
return nil
}

assert.NoError(t, repo.Clone())
_, err := repo.ExecOutput("status")
assert.NoError(t, err)
assert.Equal(t, 2, *preHookCalled)
assert.Equal(t, 3, *postHookCalled)
}

// Test_GitRepository_Hooks_ExecOutput_PreFail tests the Hooks function when
// executing a command and capturing the output and the pre hook fails.
func Test_GitRepository_Hooks_ExecOutput_PreFail(t *testing.T) {
t.Parallel()
c, _, cleanup := setupCache(t)
defer cleanup()
repo, cleanup := newLocalTestRepo(t, c)
defer cleanup()
preHookCalled := new(int)
postHookCalled := new(int)
n := 0
repo.PreHook = func(*exec.Cmd) error {
*preHookCalled = n
n++
if n > 1 {
return errors.New("test")
}
return nil
}
repo.PostHook = func(*exec.Cmd, error) error {
*postHookCalled = n
n++
return nil
}

assert.NoError(t, repo.Clone())
_, err := repo.ExecOutput("status")
assert.ErrorContains(t, err, "test")
assert.Equal(t, 2, *preHookCalled)
assert.Equal(t, 1, *postHookCalled)
}

// Test_GitRepository_Hooks_ExecOutput_PostFail tests the Hooks function when
// executing a command and capturing the output and the post hook fails.
func Test_GitRepository_Hooks_ExecOutput_PostFail(t *testing.T) {
t.Parallel()
c, _, cleanup := setupCache(t)
defer cleanup()
repo, cleanup := newLocalTestRepo(t, c)
defer cleanup()
preHookCalled := new(int)
postHookCalled := new(int)
n := 0
repo.PreHook = func(*exec.Cmd) error {
*preHookCalled = n
n++
return nil
}
repo.PostHook = func(*exec.Cmd, error) error {
*postHookCalled = n
n++
if n > 2 {
return errors.New("test")
}
return nil
}

assert.NoError(t, repo.Clone())
_, err := repo.ExecOutput("status")
assert.ErrorContains(t, err, "test")
assert.Equal(t, 2, *preHookCalled)
assert.Equal(t, 3, *postHookCalled)
}
2 changes: 1 addition & 1 deletion version.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

package main

const version = "v0.0.3"
const version = "v0.0.4"

// Version returns the version.
func Version() string {
Expand Down

0 comments on commit c3c7070

Please sign in to comment.