From 143d6ae0f16f47b35387bdfcc3c00a03ee7eff36 Mon Sep 17 00:00:00 2001 From: Gurkirat Singh Date: Wed, 13 Jul 2022 00:49:34 +0530 Subject: [PATCH 01/14] fix (lib): blunder nil check in add.go --- internal/lib/add.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/lib/add.go b/internal/lib/add.go index 84f4960..c235fd9 100644 --- a/internal/lib/add.go +++ b/internal/lib/add.go @@ -15,7 +15,7 @@ func Add(hook string, cmd string) error { // check if .git exists if isExists, err := gitExists(); err == nil && !isExists { return errors.New("git not initialized") - } else if err == nil { + } else if err != nil { return err } From d4fa82c0a59109419eaafd0e060ea723a1ee6d54 Mon Sep 17 00:00:00 2001 From: Gurkirat Singh Date: Wed, 13 Jul 2022 00:50:05 +0530 Subject: [PATCH 02/14] feat (lib): implement tests for Add --- internal/lib/lib_test.go | 156 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 internal/lib/lib_test.go diff --git a/internal/lib/lib_test.go b/internal/lib/lib_test.go new file mode 100644 index 0000000..2fff9ad --- /dev/null +++ b/internal/lib/lib_test.go @@ -0,0 +1,156 @@ +package lib_test + +import ( + huskyLib "github.com/automation-co/husky/internal/lib" + "io/ioutil" + "math/rand" + "os" + "os/exec" + "path" + "strings" + "testing" + "time" +) + +// global error message declration block +const ( + nilErrorMsg = "method has return nil error" + invildContentsErrorMsg = "invalid file contents" +) + +// getRepoPath is a testing utility function to create a random directory and return its path +func getRepoPath() (string, error) { + name, err := ioutil.TempDir(os.TempDir(), "husky") + if err != nil { + return "", err + } + + return name, nil +} + +// setupRepo creates a git repository in the path +func setupRepo(path string) { + _, err := os.Stat(path) + if os.IsNotExist(err) { + return + } + + p := exec.Command("git", "init", path) + p.Start() + p.Wait() +} + +// randomString returns a random string of a specific length +func randomString(length int) string { + rand.Seed(time.Now().UnixNano()) + + alphabet := "abcdefghijklmnopqrstuvwxyz" + var sb strings.Builder + + l := len(alphabet) + + for i := 0; i < length; i++ { + c := alphabet[rand.Intn(l)] + sb.WriteByte(c) + } + + return sb.String() +} + +// TestAddInvalidHook validates that the lib.Add() will return error when invalid hook name is provided +func TestAddInvalidHook(t *testing.T) { + err := huskyLib.Add(randomString(13), randomString(20)) + + if err == nil { + t.Error(nilErrorMsg) + } else if err.Error() != "invalid hook name" { + t.Error(err) + } +} + +// TestAddNoGit validates that the lib.Add() will return error when git is not initialized +func TestAddNoGit(t *testing.T) { + currentDir, _ := os.Getwd() + repoPath, err := getRepoPath() + defer os.RemoveAll(repoPath) + defer os.Chdir(currentDir) + + if err != nil { + t.Error(err) + } else if err := os.Chdir(repoPath); err != nil { + t.Error(err) + } + + err = huskyLib.Add("pre-commit", "whoami") + if err == nil { + t.Error(nilErrorMsg) + } else if err.Error() != "git not initialized" { + t.Error(err) + } + + if err := os.Chdir(currentDir); err != nil { + t.Error(err) + } + if err := os.RemoveAll(repoPath); err != nil { + t.Error(err) + } +} + +// TestAddNoHusky validates if the lib.Add() returns error if husky is not initialized +func TestAddNoHusky(t *testing.T) { + currentDir, _ := os.Getwd() + repoPath, err := getRepoPath() + defer os.RemoveAll(repoPath) + defer os.Chdir(currentDir) + + if err != nil { + t.Error(err) + } + setupRepo(repoPath) + + if err := os.Chdir(repoPath); err != nil { + t.Error(err) + } + + if err := huskyLib.Add("pre-commit", "whoami"); err == nil { + t.Error(nilErrorMsg) + } else if err.Error() != ".husky not initialized" { + t.Error(err) + } +} + +// TestAddNoHusky validates if the lib.Add() returns error if husky is not initialized +func TestAdd(t *testing.T) { + currentDir, _ := os.Getwd() + repoPath, err := getRepoPath() + defer os.RemoveAll(repoPath) + defer os.Chdir(currentDir) + + if err != nil { + t.Error(err) + } + setupRepo(repoPath) + + if err := os.Mkdir(path.Join(repoPath, ".husky"), 0755); err != nil { + t.Error(err) + } + + if err := os.Chdir(repoPath); err != nil { + t.Error(err) + } + + if err := huskyLib.Add("pre-commit", "whoami"); err != nil { + t.Error(err) + } + + preCommit := path.Join(repoPath, ".husky", "hooks", "pre-commit") + if _, err := os.Stat(preCommit); os.IsNotExist(err) { + t.Error(err) + } + + if content, err := ioutil.ReadFile(preCommit); err != nil { + t.Error(err) + } else if "#!/bin/sh\nwhoami" != string(content) { + t.Error(invildContentsErrorMsg) + } +} From 7ea9eaf7135ae054615d4e501e56f37d6124f55a Mon Sep 17 00:00:00 2001 From: Gurkirat Singh Date: Wed, 13 Jul 2022 00:50:59 +0530 Subject: [PATCH 03/14] chore (git): include goland .idea directory in .gitignore --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 9cf5474..a21e157 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,7 @@ # Go workspace file go.work -husky \ No newline at end of file +husky + +# Goland +.idea \ No newline at end of file From 6c9c220b8da0d1f7afd3bd9d94da997545ac8066 Mon Sep 17 00:00:00 2001 From: Gurkirat Singh Date: Wed, 13 Jul 2022 01:10:22 +0530 Subject: [PATCH 04/14] chore (actions): update the golang configuration --- .github/workflows/go.yml | 44 +++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 3869c3f..760ac53 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -1,24 +1,40 @@ name: Go on: - push: - branches: [main] pull_request: - branches: [main] + branches: + - main + types: + - opened + - synchronize + push: + branches: + - main jobs: - build: - runs-on: ubuntu-latest + unit_testing: + name: "Unit Testing" + runs-on: "${{ matrix.os }}" steps: - - uses: actions/checkout@v2 - - - name: Set up Go + - name: "Clone repository" + uses: actions/checkout@v2 + - name: "Set up Go" uses: actions/setup-go@v2 with: - go-version: 1.17 - - - name: Build - run: go build -v ./... + cache: true + check-latest: true + go-version: "${{ matrix.golang }}" + - name: "Perform Unit Test" + run: "go test -v ./..." - - name: Test - run: go test -v ./... + strategy: + matrix: + golang: + - 1.15 + - 1.16 + - 1.17 + - 1.18 + os: + - ubuntu-latest + - macos-latest + - windows-latest From 6f73665a47c37781c25c970d3602d5b59edc53b5 Mon Sep 17 00:00:00 2001 From: Gurkirat Singh Date: Wed, 13 Jul 2022 01:22:16 +0530 Subject: [PATCH 05/14] chore (actions): remove go cache and add build and install step --- .github/workflows/go.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 760ac53..fca9e14 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -12,7 +12,7 @@ on: - main jobs: - unit_testing: + testing: name: "Unit Testing" runs-on: "${{ matrix.os }}" steps: @@ -21,12 +21,13 @@ jobs: - name: "Set up Go" uses: actions/setup-go@v2 with: - cache: true - check-latest: true go-version: "${{ matrix.golang }}" - name: "Perform Unit Test" run: "go test -v ./..." - + - name: "Build and Install" + run: | + go build -v ./... + go install -v ./... strategy: matrix: golang: From 045d1b4a5ea6528160cf82af4fd8483add1e451d Mon Sep 17 00:00:00 2001 From: Gurkirat Singh Date: Thu, 14 Jul 2022 01:19:47 +0530 Subject: [PATCH 06/14] improve (lib): add more error messages and fix typo --- internal/lib/lib_test.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/internal/lib/lib_test.go b/internal/lib/lib_test.go index 2fff9ad..045aa9e 100644 --- a/internal/lib/lib_test.go +++ b/internal/lib/lib_test.go @@ -14,8 +14,10 @@ import ( // global error message declration block const ( - nilErrorMsg = "method has return nil error" - invildContentsErrorMsg = "invalid file contents" + nilErrorMsg = "method has return nil error" + invalidContentsErrorMsg = "invalid file contents" + expectedDirErrorMsg = "expected the path to be a directory" + expectedFileErrorMsg = "expected the path to be a file" ) // getRepoPath is a testing utility function to create a random directory and return its path @@ -151,6 +153,6 @@ func TestAdd(t *testing.T) { if content, err := ioutil.ReadFile(preCommit); err != nil { t.Error(err) } else if "#!/bin/sh\nwhoami" != string(content) { - t.Error(invildContentsErrorMsg) + t.Error(invalidContentsErrorMsg) } } From 6310a0d765d5b65bfcadc258fd655d78511a6b2b Mon Sep 17 00:00:00 2001 From: Gurkirat Singh Date: Thu, 14 Jul 2022 01:20:22 +0530 Subject: [PATCH 07/14] fix (lib): remove const hook dirs from utils --- internal/lib/utils.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/internal/lib/utils.go b/internal/lib/utils.go index 44562fa..3463bce 100644 --- a/internal/lib/utils.go +++ b/internal/lib/utils.go @@ -4,11 +4,6 @@ import ( "os" ) -const ( - huskyHooksDir = ".husky/hooks" - gitHooksDir = ".git/hooks" -) - // git hooks currently supported var validHooks = []string{ "applypatch-msg", From e82b1c982dc7be83df98e2a04093138921958f00 Mon Sep 17 00:00:00 2001 From: Gurkirat Singh Date: Thu, 14 Jul 2022 01:20:57 +0530 Subject: [PATCH 08/14] feat (lib): getHuskyHooksDir and getGitHooksDir to get the husky and git hooks directory dynamically --- internal/lib/utils.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/internal/lib/utils.go b/internal/lib/utils.go index 3463bce..b1ba410 100644 --- a/internal/lib/utils.go +++ b/internal/lib/utils.go @@ -2,6 +2,7 @@ package lib import ( "os" + "path" ) // git hooks currently supported @@ -65,3 +66,23 @@ func huskyExists() (bool, error) { return true, nil } + +// getHuskyHooksDir will return the relative or absolute .husky hooks directory +func getHuskyHooksDir(relative bool) string { + if relative { + return path.Join(".husky", "hooks") + } + + cwd, _ := os.Getwd() + return path.Join(cwd, ".husky", "hooks") +} + +// getGitHooksDir will return the relative or absolute .git hooks directory +func getGitHooksDir(relative bool) string { + if relative { + return path.Join(".git", "hooks") + } + + cwd, _ := os.Getwd() + return path.Join(cwd, ".git", "hooks") +} From e011c1d7683ba57a1f1d37c97eaedbb347cca209 Mon Sep 17 00:00:00 2001 From: Gurkirat Singh Date: Thu, 14 Jul 2022 01:24:51 +0530 Subject: [PATCH 09/14] improve (lib): optimize hooks creation in init --- internal/lib/init.go | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/internal/lib/init.go b/internal/lib/init.go index 679ef66..1c9511d 100644 --- a/internal/lib/init.go +++ b/internal/lib/init.go @@ -20,13 +20,8 @@ func Init() error { return err } - // if not, create .husky - err := os.Mkdir(".husky", 0755) - if err != nil { - return err - } - - err = os.Mkdir(".husky/hooks", 0755) + // if not, create .husky/hooks + err := os.MkdirAll(getHuskyHooksDir(true), 0755) if err != nil { return err } From 63de65ea286d8f470026f91a387fbd86dd99affd Mon Sep 17 00:00:00 2001 From: Gurkirat Singh Date: Thu, 14 Jul 2022 01:25:24 +0530 Subject: [PATCH 10/14] improve (lib): implement getGitHooksDir and getGitHooksDir in commands --- internal/lib/add.go | 9 +++++---- internal/lib/init.go | 3 ++- internal/lib/install.go | 3 ++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/internal/lib/add.go b/internal/lib/add.go index c235fd9..135f597 100644 --- a/internal/lib/add.go +++ b/internal/lib/add.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "os" + "path" ) func Add(hook string, cmd string) error { @@ -27,13 +28,13 @@ func Add(hook string, cmd string) error { } // check if .husky/hooks exists - _, err := os.Stat(".husky/hooks") - + _, err := os.Stat(getHuskyHooksDir(true)) + fmt.Println(err) if os.IsNotExist(err) { fmt.Println("no pre-existing hooks found") // create .husky/hooks - err = os.Mkdir(".husky/hooks", 0755) + err = os.MkdirAll(getHuskyHooksDir(true), 0755) if err != nil { return err } @@ -42,7 +43,7 @@ func Add(hook string, cmd string) error { } // create hook - file, err := os.Create(".husky/hooks/" + hook) + file, err := os.Create(path.Join(getHuskyHooksDir(true), hook)) if err != nil { return err } diff --git a/internal/lib/init.go b/internal/lib/init.go index 1c9511d..83f9d73 100644 --- a/internal/lib/init.go +++ b/internal/lib/init.go @@ -3,6 +3,7 @@ package lib import ( "errors" "os" + "path" ) func Init() error { @@ -27,7 +28,7 @@ func Init() error { } // create default pre-commit hook - file, err := os.Create(".husky/hooks/pre-commit") + file, err := os.Create(path.Join(getHuskyHooksDir(true), "pre-commit")) if err != nil { return err } diff --git a/internal/lib/install.go b/internal/lib/install.go index 4c8b51a..84efacb 100644 --- a/internal/lib/install.go +++ b/internal/lib/install.go @@ -24,8 +24,9 @@ func Install() error { return err } + gitHooksDir, huskyHooksDir := getGitHooksDir(true), getGitHooksDir(true) // check if .husky/hooks exists - _, err := os.Stat(".husky/hooks") + _, err := os.Stat(huskyHooksDir) if os.IsNotExist(err) { return errors.New("no hooks found") } From 6d1f828f777e68ddcada86cf0376821cd6f4608e Mon Sep 17 00:00:00 2001 From: Gurkirat Singh Date: Thu, 14 Jul 2022 01:26:02 +0530 Subject: [PATCH 11/14] feat (lib): added tests for init command --- internal/lib/lib_test.go | 93 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/internal/lib/lib_test.go b/internal/lib/lib_test.go index 045aa9e..55f441a 100644 --- a/internal/lib/lib_test.go +++ b/internal/lib/lib_test.go @@ -156,3 +156,96 @@ func TestAdd(t *testing.T) { t.Error(invalidContentsErrorMsg) } } + +// TestInitNoGit validates that the lib.Init() will return error when git is not initialized +func TestInitNoGit(t *testing.T) { + currentDir, _ := os.Getwd() + repoPath, err := getRepoPath() + defer os.RemoveAll(repoPath) + defer os.Chdir(currentDir) + + if err != nil { + t.Error(err) + } else if err := os.Chdir(repoPath); err != nil { + t.Error(err) + } + + err = huskyLib.Init() + if err == nil { + t.Error(nilErrorMsg) + } else if err.Error() != "git not initialized" { + t.Error(err) + } + + if err := os.Chdir(currentDir); err != nil { + t.Error(err) + } + if err := os.RemoveAll(repoPath); err != nil { + t.Error(err) + } +} + +// TestAddNoHusky validates if the lib.Add() returns error if husky is not initialized +func TestInitHuskyExists(t *testing.T) { + currentDir, _ := os.Getwd() + repoPath, err := getRepoPath() + defer os.RemoveAll(repoPath) + defer os.Chdir(currentDir) + + if err != nil { + t.Error(err) + } + setupRepo(repoPath) + + if err := os.Mkdir(path.Join(repoPath, ".husky"), 0755); err != nil { + t.Error(err) + } + + if err := os.Chdir(repoPath); err != nil { + t.Error(err) + } + + if err := huskyLib.Init(); err == nil { + t.Error(nilErrorMsg) + } else if err.Error() != ".husky already exist" { + t.Error(err) + } +} + +func TestInit(t *testing.T) { + currentDir, _ := os.Getwd() + repoPath, err := getRepoPath() + defer os.RemoveAll(repoPath) + defer os.Chdir(currentDir) + + if err != nil { + t.Error(err) + } + setupRepo(repoPath) + + if err := os.Chdir(repoPath); err != nil { + t.Error(err) + } + + if err := huskyLib.Init(); err != nil { + t.Error(err) + } + + if stat, err := os.Stat(path.Join(repoPath, ".husky", "hooks")); err != nil && !os.IsExist(err) { + t.Error(err) + } else if !stat.IsDir() { + t.Error(expectedDirErrorMsg) + } + + if stat, err := os.Stat(path.Join(repoPath, ".husky", "hooks", "pre-commit")); err != nil && !os.IsExist(err) { + t.Error(err) + } else if stat.IsDir() { + t.Error(expectedFileErrorMsg) + } + + if content, err := ioutil.ReadFile(path.Join(repoPath, ".husky", "hooks", "pre-commit")); err != nil { + t.Error(err) + } else if "#!/bin/sh" != string(content) { + t.Error(invalidContentsErrorMsg) + } +} From f2c28bded6bb3e7643efbc2686b3b6645b546863 Mon Sep 17 00:00:00 2001 From: Gurkirat Singh Date: Thu, 14 Jul 2022 01:32:02 +0530 Subject: [PATCH 12/14] improve (lib): add documentation for TestInit function --- internal/lib/lib_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/lib/lib_test.go b/internal/lib/lib_test.go index 55f441a..94e88f3 100644 --- a/internal/lib/lib_test.go +++ b/internal/lib/lib_test.go @@ -212,6 +212,8 @@ func TestInitHuskyExists(t *testing.T) { } } +// TestInit validates if the lib.Init() function runs accurately or not. +// It will skip the testing of the lib.Install() function in the end. func TestInit(t *testing.T) { currentDir, _ := os.Getwd() repoPath, err := getRepoPath() From b3299b0401dd39e9a3a2199c38c615e1f665bd08 Mon Sep 17 00:00:00 2001 From: Gurkirat Singh Date: Thu, 14 Jul 2022 01:58:23 +0530 Subject: [PATCH 13/14] fix (lib): rename second getGitHooksDir to getHuskyHooksDir --- internal/lib/install.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/lib/install.go b/internal/lib/install.go index 84efacb..f258d6a 100644 --- a/internal/lib/install.go +++ b/internal/lib/install.go @@ -24,7 +24,7 @@ func Install() error { return err } - gitHooksDir, huskyHooksDir := getGitHooksDir(true), getGitHooksDir(true) + gitHooksDir, huskyHooksDir := getGitHooksDir(true), getHuskyHooksDir(true) // check if .husky/hooks exists _, err := os.Stat(huskyHooksDir) if os.IsNotExist(err) { From 646d591938cd15bfdfa9db4962c1e4ad35591ee7 Mon Sep 17 00:00:00 2001 From: Gurkirat Singh Date: Thu, 14 Jul 2022 01:59:34 +0530 Subject: [PATCH 14/14] feat (lib): add tests for Install command --- internal/lib/lib_test.go | 121 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/internal/lib/lib_test.go b/internal/lib/lib_test.go index 94e88f3..9976cb1 100644 --- a/internal/lib/lib_test.go +++ b/internal/lib/lib_test.go @@ -251,3 +251,124 @@ func TestInit(t *testing.T) { t.Error(invalidContentsErrorMsg) } } + +// TestInstallNoGit validates that the lib.Install() will return error when git is not initialized +func TestInstallNoGit(t *testing.T) { + currentDir, _ := os.Getwd() + repoPath, err := getRepoPath() + defer os.RemoveAll(repoPath) + defer os.Chdir(currentDir) + + if err != nil { + t.Error(err) + } else if err := os.Chdir(repoPath); err != nil { + t.Error(err) + } + + err = huskyLib.Install() + if err == nil { + t.Error(nilErrorMsg) + } else if err.Error() != "git not initialized" { + t.Error(err) + } + + if err := os.Chdir(currentDir); err != nil { + t.Error(err) + } + if err := os.RemoveAll(repoPath); err != nil { + t.Error(err) + } +} + +// TestInstallNoHusky validates if the lib.Install() returns error if husky is not initialized +func TestInstallNoHusky(t *testing.T) { + currentDir, _ := os.Getwd() + repoPath, err := getRepoPath() + defer os.RemoveAll(repoPath) + defer os.Chdir(currentDir) + + if err != nil { + t.Error(err) + } + setupRepo(repoPath) + + if err := os.Chdir(repoPath); err != nil { + t.Error(err) + } + + if err := huskyLib.Install(); err == nil { + t.Error(nilErrorMsg) + } else if err.Error() != ".husky not initialized" { + t.Error(err) + } +} + +// TestInstallNoHuskyHooks validates if lib.Install() returns error when .husky/hooks directory doesn't exists +func TestInstallNoHuskyHooks(t *testing.T) { + currentDir, _ := os.Getwd() + repoPath, err := getRepoPath() + defer os.RemoveAll(repoPath) + defer os.Chdir(currentDir) + + if err != nil { + t.Error(err) + } + setupRepo(repoPath) + + if err := os.Mkdir(path.Join(repoPath, ".husky"), 0755); err != nil { + t.Error(err) + } + + if err := os.Chdir(repoPath); err != nil { + t.Error(err) + } + + if err := huskyLib.Install(); err == nil { + t.Error(nilErrorMsg) + } else if err.Error() != "no hooks found" { + t.Error(err) + } +} + +// TestInstall validates everything lib.Install() has done is correct +func TestInstall(t *testing.T) { + currentDir, _ := os.Getwd() + repoPath, err := getRepoPath() + //defer os.RemoveAll(repoPath) + defer os.Chdir(currentDir) + if err != nil { + t.Error(err) + } + setupRepo(repoPath) + + if err := os.MkdirAll(path.Join(repoPath, ".husky", "hooks"), 0755); err != nil { + t.Error(err) + } + + gibbrish := randomString(32) + var file *os.File + if file, err = os.Create(path.Join(repoPath, ".husky", "hooks", "pre-commit")); err != nil { + t.Error(err) + } else if _, err := file.WriteString(gibbrish); err != nil { + t.Error(err) + } + file.Close() + + if err := os.Chdir(repoPath); err != nil { + t.Error(err) + } + + if err := huskyLib.Install(); err != nil { + t.Error(err) + } + + if file, err := os.Stat(path.Join(".git", "hooks", "pre-commit")); err != nil && !os.IsExist(err) { + t.Error(err) + } else if file.IsDir() { + t.Error(expectedDirErrorMsg) + } else if content, err := ioutil.ReadFile(path.Join(".git", "hooks", "pre-commit")); err != nil { + t.Error(err) + } else if string(content) != gibbrish { + t.Error(invalidContentsErrorMsg) + } +}