Skip to content

Commit

Permalink
Add options to IO
Browse files Browse the repository at this point in the history
To pass the original hook stdIn as --input option the
IO had to be made aware of the command options.
And all detectors da to switch from reading stdIn to asking
the IO for the --input option.

This way no hook execution is depending on stdIn and all hook
actions can require user input if deemed necessary.
  • Loading branch information
sebastianfeldmann committed Apr 12, 2024
1 parent ab63d2b commit 736a5da
Show file tree
Hide file tree
Showing 17 changed files with 100 additions and 30 deletions.
10 changes: 9 additions & 1 deletion commands/hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,15 +80,23 @@ func setupHookSubCommand(hook string) *cobra.Command {
DisplayCommandError(errRepo)
}

input, _ := cmd.Flags().GetString("input")
opts := map[string]string{
"input": input,
}

io.ColorStatus(conf.AnsiColors())
appIO := io.NewDefaultIO(conf.Verbosity(), mapArgs(info.HookArguments(hook), args, hook))
appIO := io.NewDefaultIO(conf.Verbosity(), opts, mapArgs(info.HookArguments(hook), args, hook))
runner := exec.NewHookRunner(hook, appIO, conf, repo)

errRun := runner.Run()
if errRun != nil {
os.Exit(1)
}
},
}
var input = ""
cmd.Flags().StringP("input", "i", input, "original hook stdIn")

configurationAware(cmd)
repositoryAware(cmd)
Expand Down
2 changes: 1 addition & 1 deletion commands/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func setupInitCommand() *cobra.Command {
force, _ := cmd.Flags().GetBool("force")
config, _ := cmd.Flags().GetString("configuration")

appIO := io.NewDefaultIO(configuration.MapVerbosity(getVerbosity(cmd)), make(map[string]string))
appIO := io.NewDefaultIO(configuration.MapVerbosity(getVerbosity(cmd)), map[string]string{}, make(map[string]string))

initializer := exec.NewInitializer(appIO)
initializer.UseConfig(config)
Expand Down
2 changes: 1 addition & 1 deletion commands/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func setupInstallCommand() *cobra.Command {
DisplayCommandError(errRepo)
}

appIO := io.NewDefaultIO(conf.Verbosity(), make(map[string]string))
appIO := io.NewDefaultIO(conf.Verbosity(), map[string]string{}, map[string]string{})

installer := exec.NewInstaller(appIO, conf, repo)
installer.SkipExisting(skip)
Expand Down
2 changes: 1 addition & 1 deletion commands/uninstall.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func setupUninstallCommand() *cobra.Command {
DisplayCommandError(errRepo)
}

appIO := io.NewDefaultIO(conf.Verbosity(), make(map[string]string))
appIO := io.NewDefaultIO(conf.Verbosity(), map[string]string{}, map[string]string{})

unInstaller := exec.NewUninstaller(appIO, conf, repo)
unInstaller.Force(force)
Expand Down
12 changes: 11 additions & 1 deletion exec/installer.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,18 @@ func (i *Installer) HookTemplate() string {
"# installed by CaptainHook {{ .VERSION }}\n" +
"\n" +
"INTERACTIVE=\"{{ if .INTERACTION }}--no-interaction {{ end }}\"\n" +
"# read original hook stdIn to pass it in as --input option\n" +
"input=$(cat)\n" +
"\n" +
"{{ .RUN_PATH }}captainhook $INTERACTIVE--configuration={{ .CONFIGURATION }} hook {{ .HOOK_NAME }} \"$@\" <&0\n\n"
"if [ -t 1 ]; then\n" +
" # If we're in a terminal, redirect stdout and stderr to /dev/tty and\n" +
" # read stdin from /dev/tty. Allow interactive mode for CaptainHook.\n" +
" exec >/dev/tty 2>/dev/tty </dev/tty\n" +
" INTERACTIVE=\"\"\n" +
"fi\n" +
"\n" +
"{{ .RUN_PATH }}captainhook $INTERACTIVE--configuration={{ .CONFIGURATION }} " +
"hook {{ .HOOK_NAME }} --input=\"$input\" \"$@\" <&0\n\n"
}

func NewInstaller(appIO io.IO, config *configuration.Configuration, repo git.Repo) *Installer {
Expand Down
8 changes: 4 additions & 4 deletions hooks/conditions/filechanged/all_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
func TestAllFilesAreChanged(t *testing.T) {
inOut := test.CreateFakeIO()
inOut.SetArguments(map[string]string{"command": "pre-push"})
inOut.SetStdIn([]string{"refs/heads/main 12345 refs/heads/main 09876"})
inOut.SetOptions(map[string]string{"input": "refs/heads/main 12345 refs/heads/main 09876"})
conf := test.CreateFakeConfig()
repo := test.CreateFakeRepo()
repo.SetFiles([]string{"foo", "bar", "baz"})
Expand All @@ -26,7 +26,7 @@ func TestAllFilesAreChanged(t *testing.T) {
func TestNotAllChangedFiles(t *testing.T) {
inOut := test.CreateFakeIO()
inOut.SetArguments(map[string]string{"command": "pre-push"})
inOut.SetStdIn([]string{"12345 refs/heads/main 09876 refs/heads/main"})
inOut.SetOptions(map[string]string{"input": "refs/heads/main 12345 refs/heads/main 09876"})
conf := test.CreateFakeConfig()
repo := test.CreateFakeRepo()
repo.SetFiles([]string{"foo", "bar", "baz"})
Expand All @@ -43,7 +43,7 @@ func TestNotAllChangedFiles(t *testing.T) {
func TestAllChangedFilesDetectionFailed(t *testing.T) {
inOut := test.CreateFakeIO()
inOut.SetArguments(map[string]string{"command": "pre-push"})
inOut.SetStdIn([]string{""})
inOut.SetOptions(map[string]string{"input": ""})
conf := test.CreateFakeConfig()
repo := test.CreateFakeRepo()
repo.SetFilesError(true)
Expand All @@ -60,7 +60,7 @@ func TestAllChangedFilesDetectionFailed(t *testing.T) {
func TestAllChangedFilesFailed(t *testing.T) {
inOut := test.CreateFakeIO()
inOut.SetArguments(map[string]string{"command": "pre-push"})
inOut.SetStdIn([]string{"12345 refs/heads/main 09876 refs/heads/main"})
inOut.SetOptions(map[string]string{"input": "refs/heads/main 12345 refs/heads/main 09876"})
conf := test.CreateFakeConfig()
repo := test.CreateFakeRepo()
repo.SetFilesError(true)
Expand Down
10 changes: 5 additions & 5 deletions hooks/conditions/filechanged/any_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
func TestAnyFilesAreStaged(t *testing.T) {
inOut := test.CreateFakeIO()
inOut.SetArguments(map[string]string{"command": "pre-push"})
inOut.SetStdIn([]string{"refs/heads/main 12345 refs/heads/main 09876"})
inOut.SetOptions(map[string]string{"input": "refs/heads/main 12345 refs/heads/main 09876"})
conf := test.CreateFakeConfig()
repo := test.CreateFakeRepo()
repo.SetFiles([]string{"foo", "bar", "baz"})
Expand All @@ -19,14 +19,14 @@ func TestAnyFilesAreStaged(t *testing.T) {

action := NewAny(inOut, conf, repo)
if !action.IsTrue(condition) {
t.Errorf("All files should be staged")
t.Errorf("At least one file should be staged")
}
}

func TestAnyNoFilesAreStaged(t *testing.T) {
inOut := test.CreateFakeIO()
inOut.SetArguments(map[string]string{"command": "pre-push"})
inOut.SetStdIn([]string{"refs/heads/main 12345 refs/heads/main 09876"})
inOut.SetOptions(map[string]string{"input": "refs/heads/main 12345 refs/heads/main 09876"})
conf := test.CreateFakeConfig()
repo := test.CreateFakeRepo()
repo.SetFiles([]string{"foo", "bar", "baz"})
Expand All @@ -43,7 +43,7 @@ func TestAnyNoFilesAreStaged(t *testing.T) {
func TestAnyStagedFilesFailed(t *testing.T) {
inOut := test.CreateFakeIO()
inOut.SetArguments(map[string]string{"command": "pre-push"})
inOut.SetStdIn([]string{"refs/heads/main 12345 refs/heads/main 09876"})
inOut.SetOptions(map[string]string{"input": "refs/heads/main 12345 refs/heads/main 09876"})
conf := test.CreateFakeConfig()
repo := test.CreateFakeRepo()
repo.SetFilesError(true)
Expand All @@ -60,7 +60,7 @@ func TestAnyStagedFilesFailed(t *testing.T) {
func TestAnyStagedFilesDetectFailed(t *testing.T) {
inOut := test.CreateFakeIO()
inOut.SetArguments(map[string]string{"command": "pre-push"})
inOut.SetStdIn([]string{""})
inOut.SetOptions(map[string]string{"input": ""})
conf := test.CreateFakeConfig()
repo := test.CreateFakeRepo()

Expand Down
4 changes: 2 additions & 2 deletions hooks/input/files_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func TestStagedOrChangedFilesWithStaged(t *testing.T) {
func TestStagedOrChangedFilesWithChanged(t *testing.T) {
inOut := test.CreateFakeIO()
inOut.SetArguments(map[string]string{"command": "pre-push"})
inOut.SetStdIn([]string{"refs/heads/main 12345 refs/heads/main 09876"})
inOut.SetOptions(map[string]string{"input": "refs/heads/main 12345 refs/heads/main 09876"})
repo := test.CreateFakeRepo()
repo.SetFiles([]string{"foo", "bar", "baz"})

Expand All @@ -35,7 +35,7 @@ func TestStagedOrChangedFilesWithChanged(t *testing.T) {
func TestStagedOrChangedFilesWithBrokenInput(t *testing.T) {
inOut := test.CreateFakeIO()
inOut.SetArguments(map[string]string{"command": "pre-push"})
inOut.SetStdIn([]string{""})
inOut.SetOptions(map[string]string{"input": ""})
repo := test.CreateFakeRepo()
repo.SetFiles([]string{"foo", "bar", "baz"})

Expand Down
4 changes: 2 additions & 2 deletions hooks/input/ranges.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ var (
detectors = map[string]func(appIO io.IO) []*types.Range{
"pre-push": func(appIO io.IO) []*types.Range {
var ranges []*types.Range
for _, line := range appIO.StandardInput() {
for _, line := range io.SplitLines(appIO.Option("input", "")) {
if len(line) == 0 {
continue
}
Expand All @@ -37,7 +37,7 @@ var (
},
"post-rewrite": func(appIO io.IO) []*types.Range {
var ranges []*types.Range
for _, input := range appIO.StandardInput() {
for _, input := range io.SplitLines(appIO.Option("input", "")) {
if len(input) > 0 {
parts := strings.Split(strings.TrimSpace(input), " ")
id := "HEAD@{1}"
Expand Down
3 changes: 2 additions & 1 deletion hooks/placeholder/args_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ import (

func TestArgs(t *testing.T) {
argMap := map[string]string{"message-file": "bar.txt"}
optMap := map[string]string{"input": ""}
expected := "bar.txt"

config := configuration.NewConfiguration("foo", false)
repo, _ := git.NewRepository(".git")
ctx := app.NewContext(
io.NewDefaultIO(io.NORMAL, argMap),
io.NewDefaultIO(io.NORMAL, optMap, argMap),
config,
repo,
)
Expand Down
4 changes: 2 additions & 2 deletions hooks/placeholder/envvar_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func TestEnvVarFound(t *testing.T) {
config := configuration.NewConfiguration("foo", false)
repo, _ := git.NewRepository(".git")
ctx := app.NewContext(
io.NewDefaultIO(io.NORMAL, map[string]string{}),
io.NewDefaultIO(io.NORMAL, map[string]string{}, map[string]string{}),
config,
repo,
)
Expand All @@ -38,7 +38,7 @@ func TestEnvVarDefault(t *testing.T) {
config := configuration.NewConfiguration("foo", false)
repo, _ := git.NewRepository(".git")
ctx := app.NewContext(
io.NewDefaultIO(io.NORMAL, map[string]string{}),
io.NewDefaultIO(io.NORMAL, map[string]string{}, map[string]string{}),
config,
repo,
)
Expand Down
8 changes: 4 additions & 4 deletions hooks/placeholder/filelist_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func TestFileListDefault(t *testing.T) {
config := configuration.NewConfiguration("foo", false)
repo, _ := git.NewRepository(".git")
ctx := app.NewContext(
io.NewDefaultIO(io.NORMAL, map[string]string{}),
io.NewDefaultIO(io.NORMAL, map[string]string{}, map[string]string{}),
config,
repo,
)
Expand All @@ -35,7 +35,7 @@ func TestFileListInDirectory(t *testing.T) {
config := configuration.NewConfiguration("foo", false)
repo, _ := git.NewRepository(".git")
ctx := app.NewContext(
io.NewDefaultIO(io.NORMAL, map[string]string{}),
io.NewDefaultIO(io.NORMAL, map[string]string{}, map[string]string{}),
config,
repo,
)
Expand All @@ -55,7 +55,7 @@ func TestFileListOfType(t *testing.T) {
config := configuration.NewConfiguration("foo", false)
repo, _ := git.NewRepository(".git")
ctx := app.NewContext(
io.NewDefaultIO(io.NORMAL, map[string]string{}),
io.NewDefaultIO(io.NORMAL, map[string]string{}, map[string]string{}),
config,
repo,
)
Expand All @@ -75,7 +75,7 @@ func TestFileListSeparatedBy(t *testing.T) {
config := configuration.NewConfiguration("foo", false)
repo, _ := git.NewRepository(".git")
ctx := app.NewContext(
io.NewDefaultIO(io.NORMAL, map[string]string{}),
io.NewDefaultIO(io.NORMAL, map[string]string{}, map[string]string{}),
config,
repo,
)
Expand Down
9 changes: 9 additions & 0 deletions io/collectorio.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,18 @@ func (c *CollectorIO) Verbosity() int {
return c.verbosity
}

func (c *CollectorIO) Options() map[string]string {
return c.input.Options()
}

func (c *CollectorIO) Option(name, defaultValue string) string {
return c.input.Option(name, defaultValue)
}

func (c *CollectorIO) Arguments() map[string]string {
return c.input.Arguments()
}

func (c *CollectorIO) Argument(name, defaultValue string) string {
return c.input.Argument(name, defaultValue)
}
Expand Down
12 changes: 10 additions & 2 deletions io/defaultio.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,23 @@ type DefaultIO struct {
input Input
}

func NewDefaultIO(verbosity int, arguments map[string]string) *DefaultIO {
io := DefaultIO{verbosity: verbosity, input: NewStdIn(arguments)}
func NewDefaultIO(verbosity int, options map[string]string, arguments map[string]string) *DefaultIO {
io := DefaultIO{verbosity: verbosity, input: NewStdIn(options, arguments)}
return &io
}

func (d *DefaultIO) Verbosity() int {
return d.verbosity
}

func (d *DefaultIO) Options() map[string]string {
return d.input.Options()
}

func (d *DefaultIO) Option(name, defaultValue string) string {
return d.input.Option(name, defaultValue)
}

func (d *DefaultIO) Arguments() map[string]string {
return d.input.Arguments()
}
Expand Down
19 changes: 17 additions & 2 deletions io/input.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (

type Input interface {
Data() []string
Option(name, defaultValue string) string
Options() map[string]string
Argument(name, defaultValue string) string
Arguments() map[string]string
Ask(message, defaultValue string) string
Expand All @@ -16,6 +18,7 @@ type Input interface {
type StdIn struct {
stdInLoaded bool
stdInData []string
options map[string]string
arguments map[string]string
}

Expand All @@ -39,6 +42,18 @@ func (s *StdIn) Arguments() map[string]string {
return s.arguments
}

func (s *StdIn) Option(name, defaultValue string) string {
value, ok := s.options[name]
if !ok {
value = defaultValue
}
return value
}

func (s *StdIn) Options() map[string]string {
return s.options
}

func (s *StdIn) Ask(message, defaultValue string) string {
value, err := s.askForUserInput(message)
if err != nil {
Expand Down Expand Up @@ -81,6 +96,6 @@ func (s *StdIn) isPiped() bool {
return (stat.Mode() & os.ModeCharDevice) == 0
}

func NewStdIn(args map[string]string) *StdIn {
return &StdIn{arguments: args, stdInLoaded: false}
func NewStdIn(opts map[string]string, args map[string]string) *StdIn {
return &StdIn{options: opts, arguments: args, stdInLoaded: false}
}
2 changes: 2 additions & 0 deletions io/io.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ const (

type IO interface {
Verbosity() int
Options() map[string]string
Option(name, defaultValue string) string
Arguments() map[string]string
Argument(name, defaultValue string) string
StandardInput() []string
Expand Down
19 changes: 18 additions & 1 deletion test/io.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import "github.com/captainhook-go/captainhook/io"

type IOMock struct {
stdIn []string
opts map[string]string
args map[string]string
Out []string
}
Expand All @@ -16,10 +17,26 @@ func (inOut *IOMock) SetArguments(args map[string]string) {
inOut.args = args
}

func (inOut *IOMock) SetOptions(args map[string]string) {
inOut.opts = args
}

func (inOut *IOMock) Verbosity() int {
return 0
}

func (inOut *IOMock) Options() map[string]string {
return map[string]string{}
}

func (inOut *IOMock) Option(name, defaultValue string) string {
val, ok := inOut.opts[name]
if !ok {
return defaultValue
}
return val
}

func (inOut *IOMock) Arguments() map[string]string {
return map[string]string{}
}
Expand All @@ -37,7 +54,7 @@ func (inOut *IOMock) StandardInput() []string {
}

func (inOut *IOMock) Input() io.Input {
return io.NewStdIn(inOut.args)
return io.NewStdIn(inOut.opts, inOut.args)
}

func (inOut *IOMock) IsInteractive() bool {
Expand Down

0 comments on commit 736a5da

Please sign in to comment.