Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 2 additions & 34 deletions internal/commands/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"fmt"
"os"
"sort"
"strings"

"github.com/devbytes-cloud/freight/internal/blueprint"
"github.com/devbytes-cloud/freight/internal/config"
Expand All @@ -18,14 +17,6 @@ import (
"gopkg.in/yaml.v3"
)

var allowHooks = map[string]struct{}{
"pre-commit": {},
"prepare-commit-msg": {},
"commit-msg": {},
"post-commit": {},
"post-checkout": {},
}

// Execute runs the root command and handles any errors that occur during execution.
func Execute() {
if err := NewRootCmd().Execute(); err != nil {
Expand Down Expand Up @@ -84,7 +75,7 @@ func NewRootCmd() *cobra.Command {
} else if len(freightConfig.Allow) == 0 {
// If no user allow and no existing allow, use default all hooks
var allHooks []string
for h := range allowHooks {
for h := range githooks.AllowedGitHooks() {
allHooks = append(allHooks, h)
}
sort.Strings(allHooks)
Expand All @@ -98,7 +89,7 @@ func NewRootCmd() *cobra.Command {
pterm.Debug.Printfln("Effective allow: %v", freightConfig.Allow)
pterm.Debug.Printfln("Hooks to setup: %v", hooksToSetup)

validatedAllow, err := validateAllowHooks(hooksToSetup)
validatedAllow, err := validate.GitHooks(hooksToSetup)
if err != nil {
cmd.PrintErrln(err)
os.Exit(1)
Expand Down Expand Up @@ -224,26 +215,3 @@ func installBinary() error {
pterm.Success.Println("✔ Installed conductor successfully")
return nil
}

// validateAllowHooks validates the provided allow hooks and returns a map of valid hooks.
func validateAllowHooks(allow []string) (map[string]struct{}, error) {
if len(allow) == 0 {
pterm.Debug.Println("No hooks provided, using default allowed hooks")
return allowHooks, nil
}

inputHooks := map[string]struct{}{}
var invalidHooks []string
for _, v := range allow {
if _, ok := allowHooks[v]; !ok {
invalidHooks = append(invalidHooks, v)
}
inputHooks[v] = struct{}{}
}

if len(invalidHooks) > 0 {
return nil, fmt.Errorf("invalid hook types: %s", strings.Join(invalidHooks, ", "))
}

return inputHooks, nil
}
96 changes: 0 additions & 96 deletions internal/commands/root_test.go
Original file line number Diff line number Diff line change
@@ -1,97 +1 @@
package commands

import (
"fmt"
"testing"

"github.com/stretchr/testify/assert"
)

type hookStructure struct {
inputHooks []string
expectedHooks map[string]struct{}
expectedErr bool
expectedErrMgs error
}

func TestValidateAllowHooks(t *testing.T) {
testData := map[string]hookStructure{
"no input hooks": {
expectedErr: false,
inputHooks: []string{},
expectedHooks: map[string]struct{}{
"pre-commit": {},
"prepare-commit-msg": {},
"commit-msg": {},
"post-commit": {},
"post-checkout": {},
},
},
"only pre-commit hook": {
expectedErr: false,
inputHooks: []string{"pre-commit"},
expectedHooks: map[string]struct{}{
"pre-commit": {},
},
},
"invalid hook name hook": {
expectedErr: true,
inputHooks: []string{"invalid hook name"},
expectedErrMgs: fmt.Errorf("invalid hook types: invalid hook name"),
expectedHooks: nil,
},
"multiple valid hooks": {
expectedErr: false,
inputHooks: []string{"pre-commit", "commit-msg", "post-checkout"},
expectedHooks: map[string]struct{}{
"pre-commit": {},
"commit-msg": {},
"post-checkout": {},
},
},
"multiple invalid hooks": {
expectedErr: true,
inputHooks: []string{"invalid1", "invalid2"},
expectedErrMgs: fmt.Errorf("invalid hook types: invalid1, invalid2"),
expectedHooks: nil,
},
"mix of valid and invalid hooks": {
expectedErr: true,
inputHooks: []string{"pre-commit", "invalid-hook"},
expectedErrMgs: fmt.Errorf("invalid hook types: invalid-hook"),
expectedHooks: nil,
},
"all valid hooks explicitly provided": {
expectedErr: false,
inputHooks: []string{"pre-commit", "prepare-commit-msg", "commit-msg", "post-commit", "post-checkout"},
expectedHooks: map[string]struct{}{
"pre-commit": {},
"prepare-commit-msg": {},
"commit-msg": {},
"post-commit": {},
"post-checkout": {},
},
},
"duplicate valid hooks": {
expectedErr: false,
inputHooks: []string{"pre-commit", "pre-commit"},
expectedHooks: map[string]struct{}{
"pre-commit": {},
},
},
}

for name, test := range testData {
t.Run(name, func(t *testing.T) {
resp, err := validateAllowHooks(test.inputHooks)

if test.expectedErr {
assert.Error(t, err)
assert.EqualError(t, err, test.expectedErrMgs.Error())
} else {
assert.NoError(t, err)
assert.Equal(t, test.expectedHooks, resp)
}
})
}
}
18 changes: 18 additions & 0 deletions internal/githooks/githooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,21 @@ func generateHooks(hook []string) []GitHook {
func hookPath(hookType string) string {
return filepath.Join(hooksBaseDir, hookType)
}

// AllowedGitHooks returns a slice of allowed Git hook types.
func AllowedGitHooks() map[string]struct{} {
checkout := getCheckoutHooks()
commit := getCommitHook()

allowedHooks := make(map[string]struct{}, len(checkout)+len(commit))

for _, hook := range checkout {
allowedHooks[hook] = struct{}{}
}

for _, hook := range commit {
allowedHooks[hook] = struct{}{}
}

return allowedHooks
}
20 changes: 20 additions & 0 deletions internal/githooks/githooks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,23 @@ func TestNewGitHooks(t *testing.T) {
assert.Equal(t, gitHookTemplate, hook.Template, "checkout hook Template should match gitHookTemplate")
}
}

// TestAllowedGitHooks verifies that AllowedGitHooks returns the expected map of allowed Git hooks.
func TestAllowedGitHooks(t *testing.T) {
allowedHooks := AllowedGitHooks()

expectedHooks := []string{
PreCommit,
PrepareCommitMsg,
CommitMsg,
PostCommit,
PostCheckout,
}

assert.Len(t, allowedHooks, len(expectedHooks), "AllowedGitHooks should return the correct number of hooks")

for _, hook := range expectedHooks {
_, exists := allowedHooks[hook]
assert.True(t, exists, "Hook %s should be in the allowed hooks map", hook)
}
}
29 changes: 29 additions & 0 deletions internal/validate/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import (
"fmt"
"os"
"path/filepath"
"strings"

"github.com/devbytes-cloud/freight/internal/githooks"
"github.com/pterm/pterm"
)

var (
Expand Down Expand Up @@ -35,3 +39,28 @@ func CurrentWD() (string, error) {

return dir, nil
}

// GitHooks validates the provided allow hooks and returns a map of valid hooks.
func GitHooks(allow []string) (map[string]struct{}, error) {
allowedGitHooks := githooks.AllowedGitHooks()
if len(allow) == 0 {
pterm.Debug.Println("No hooks provided, using default allowed hooks")
return allowedGitHooks, nil
}

inputHooks := map[string]struct{}{}
var invalidHooks []string
for _, v := range allow {
if _, ok := allowedGitHooks[v]; !ok {
invalidHooks = append(invalidHooks, v)
continue
}
inputHooks[v] = struct{}{}
}

if len(invalidHooks) > 0 {
return nil, fmt.Errorf("invalid hook types: %s", strings.Join(invalidHooks, ", "))
}

return inputHooks, nil
}
90 changes: 90 additions & 0 deletions internal/validate/validate_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package validate_test

import (
"fmt"
"os"
"path/filepath"
"testing"
Expand All @@ -10,6 +11,13 @@ import (
"github.com/stretchr/testify/require"
)

type hookStructure struct {
inputHooks []string
expectedHooks map[string]struct{}
expectedErr bool
expectedErrMgs error
}

func TestGitDirs(t *testing.T) {
tt := []struct {
name string
Expand Down Expand Up @@ -60,3 +68,85 @@ func TestCurrentWD(t *testing.T) {
assert.NoError(t, err)
assert.NotEmpty(t, wd)
}

func TestValidateAllowHooks(t *testing.T) {
testData := map[string]hookStructure{
"no input hooks": {
expectedErr: false,
inputHooks: []string{},
expectedHooks: map[string]struct{}{
"pre-commit": {},
"prepare-commit-msg": {},
"commit-msg": {},
"post-commit": {},
"post-checkout": {},
},
},
"only pre-commit hook": {
expectedErr: false,
inputHooks: []string{"pre-commit"},
expectedHooks: map[string]struct{}{
"pre-commit": {},
},
},
"invalid hook name hook": {
expectedErr: true,
inputHooks: []string{"invalid hook name"},
expectedErrMgs: fmt.Errorf("invalid hook types: invalid hook name"),
expectedHooks: nil,
},
"multiple valid hooks": {
expectedErr: false,
inputHooks: []string{"pre-commit", "commit-msg", "post-checkout"},
expectedHooks: map[string]struct{}{
"pre-commit": {},
"commit-msg": {},
"post-checkout": {},
},
},
"multiple invalid hooks": {
expectedErr: true,
inputHooks: []string{"invalid1", "invalid2"},
expectedErrMgs: fmt.Errorf("invalid hook types: invalid1, invalid2"),
expectedHooks: nil,
},
"mix of valid and invalid hooks": {
expectedErr: true,
inputHooks: []string{"pre-commit", "invalid-hook"},
expectedErrMgs: fmt.Errorf("invalid hook types: invalid-hook"),
expectedHooks: nil,
},
"all valid hooks explicitly provided": {
expectedErr: false,
inputHooks: []string{"pre-commit", "prepare-commit-msg", "commit-msg", "post-commit", "post-checkout"},
expectedHooks: map[string]struct{}{
"pre-commit": {},
"prepare-commit-msg": {},
"commit-msg": {},
"post-commit": {},
"post-checkout": {},
},
},
"duplicate valid hooks": {
expectedErr: false,
inputHooks: []string{"pre-commit", "pre-commit"},
expectedHooks: map[string]struct{}{
"pre-commit": {},
},
},
}

for name, test := range testData {
t.Run(name, func(t *testing.T) {
resp, err := validate.GitHooks(test.inputHooks)

if test.expectedErr {
assert.Error(t, err)
assert.EqualError(t, err, test.expectedErrMgs.Error())
} else {
assert.NoError(t, err)
assert.Equal(t, test.expectedHooks, resp)
}
})
}
}