Repository: jesseduffield/lazygit (language: go)

Issue:
custom alias in my ~/.zshrc isn't working with the lazygit's custom command option ':'
**Description**
When I try to run the custom command using lazygit's ':' option, it doesn't run any of my alias given in the ~/.zshrc

**Steps to reproduce the behavior:**
1. Go to lazygit
2. Enter custom command using ':' mode
3. Enter an alias from your ~/.zshrc (in my case, the alias is `alias gitlfs="git lfs install && git lfs pull && git lfs uninstall && git reset ."`)
4. You'll notice "bash -c gitlfs" command not found

**Desktop (please complete the following information):**
 - OS: macOS Catalina 10.15.3
 - Lazygit Version: 0.18, os=darwin, arch=amd64

Thanks



Discussion / hints:
This is because we call the shell directly, note that when in a terminal the command `zsh -c l` also does not work since it does not use the zshrc file. 
I have not found a way to force the zsh command to use the zshrc file. But we would have to either source the zshrc file ourself, or find an argument to automaticly do that.
Yeah I'm not too sure how to get around this one
@glvr182 @jesseduffield 
Couldn't $SHELL be used such that `zsh -c` is called instead of `bash -c` if this is the default shell on the users system?

Regarding aliases in `zsh -c`, they will work if you put them in `.zshenv`.
I used kinda a hacky workaround for this(or at least something like this)

i had a function in .zshrc called gitlab_deploy which also gave me 

```
+ bash -c gitlab_deploy
                                                                       
bash: gitlab_deploy: command not found
```
when running with ctrl+d from lazygit (d from deploy, get it? hah)

what i did is to move the script from .zshrc to one folder from $PATH variable (i chosed /home/name/bin)
so the file will be /home/name/bin/gitlab_deploy

and then in my lazygit/config.yml:
i moved from 
`command: 'gitlab_deploy'`
to
`command: 'zsh -c gitlab_deploy'
`
so in the end it will execute `bash -c "zsh -c gitlab_deploy"` instead of `bash -c gitlab_deploy`

Fix patch:
diff --git a/pkg/commands/git_cmd_obj_builder.go b/pkg/commands/git_cmd_obj_builder.go
index 21f300bb379..7cb4f149641 100644
--- a/pkg/commands/git_cmd_obj_builder.go
+++ b/pkg/commands/git_cmd_obj_builder.go
@@ -38,6 +38,10 @@ func (self *gitCmdObjBuilder) NewShell(cmdStr string) oscommands.ICmdObj {
 	return self.innerBuilder.NewShell(cmdStr).AddEnvVars(defaultEnvVar)
 }
 
+func (self *gitCmdObjBuilder) NewInteractiveShell(cmdStr string) oscommands.ICmdObj {
+	return self.innerBuilder.NewInteractiveShell(cmdStr).AddEnvVars(defaultEnvVar)
+}
+
 func (self *gitCmdObjBuilder) Quote(str string) string {
 	return self.innerBuilder.Quote(str)
 }
diff --git a/pkg/commands/oscommands/cmd_obj_builder.go b/pkg/commands/oscommands/cmd_obj_builder.go
index 540487ed46e..3e89ce10293 100644
--- a/pkg/commands/oscommands/cmd_obj_builder.go
+++ b/pkg/commands/oscommands/cmd_obj_builder.go
@@ -14,6 +14,8 @@ type ICmdObjBuilder interface {
 	New(args []string) ICmdObj
 	// NewShell takes a string like `git commit` and returns an executable shell command for it e.g. `sh -c 'git commit'`
 	NewShell(commandStr string) ICmdObj
+	// Like NewShell, but uses the user's shell rather than "bash", and passes -i to it
+	NewInteractiveShell(commandStr string) ICmdObj
 	// Quote wraps a string in quotes with any necessary escaping applied. The reason for bundling this up with the other methods in this interface is that we basically always need to make use of this when creating new command objects.
 	Quote(str string) string
 }
@@ -43,10 +45,23 @@ func (self *CmdObjBuilder) NewWithEnviron(args []string, env []string) ICmdObj {
 }
 
 func (self *CmdObjBuilder) NewShell(commandStr string) ICmdObj {
-	var quotedCommand string
+	quotedCommand := self.quotedCommandString(commandStr)
+	cmdArgs := str.ToArgv(fmt.Sprintf("%s %s %s", self.platform.Shell, self.platform.ShellArg, quotedCommand))
+
+	return self.New(cmdArgs)
+}
+
+func (self *CmdObjBuilder) NewInteractiveShell(commandStr string) ICmdObj {
+	quotedCommand := self.quotedCommandString(commandStr)
+	cmdArgs := str.ToArgv(fmt.Sprintf("%s %s %s %s", self.platform.InteractiveShell, self.platform.InteractiveShellArg, self.platform.ShellArg, quotedCommand))
+
+	return self.New(cmdArgs)
+}
+
+func (self *CmdObjBuilder) quotedCommandString(commandStr string) string {
 	// Windows does not seem to like quotes around the command
 	if self.platform.OS == "windows" {
-		quotedCommand = strings.NewReplacer(
+		return strings.NewReplacer(
 			"^", "^^",
 			"&", "^&",
 			"|", "^|",
@@ -54,13 +69,9 @@ func (self *CmdObjBuilder) NewShell(commandStr string) ICmdObj {
 			">", "^>",
 			"%", "^%",
 		).Replace(commandStr)
-	} else {
-		quotedCommand = self.Quote(commandStr)
 	}
 
-	cmdArgs := str.ToArgv(fmt.Sprintf("%s %s %s", self.platform.Shell, self.platform.ShellArg, quotedCommand))
-
-	return self.New(cmdArgs)
+	return self.Quote(commandStr)
 }
 
 func (self *CmdObjBuilder) CloneWithNewRunner(decorate func(ICmdObjRunner) ICmdObjRunner) *CmdObjBuilder {
diff --git a/pkg/commands/oscommands/dummies.go b/pkg/commands/oscommands/dummies.go
index 3f72a6f0652..ab528782fa6 100644
--- a/pkg/commands/oscommands/dummies.go
+++ b/pkg/commands/oscommands/dummies.go
@@ -51,11 +51,13 @@ func NewDummyCmdObjBuilder(runner ICmdObjRunner) *CmdObjBuilder {
 }
 
 var dummyPlatform = &Platform{
-	OS:              "darwin",
-	Shell:           "bash",
-	ShellArg:        "-c",
-	OpenCommand:     "open {{filename}}",
-	OpenLinkCommand: "open {{link}}",
+	OS:                  "darwin",
+	Shell:               "bash",
+	InteractiveShell:    "bash",
+	ShellArg:            "-c",
+	InteractiveShellArg: "-i",
+	OpenCommand:         "open {{filename}}",
+	OpenLinkCommand:     "open {{link}}",
 }
 
 func NewDummyOSCommandWithRunner(runner *FakeCmdObjRunner) *OSCommand {
diff --git a/pkg/commands/oscommands/os.go b/pkg/commands/oscommands/os.go
index 7771dffba5f..7f31b533239 100644
--- a/pkg/commands/oscommands/os.go
+++ b/pkg/commands/oscommands/os.go
@@ -35,11 +35,13 @@ type OSCommand struct {
 
 // Platform stores the os state
 type Platform struct {
-	OS              string
-	Shell           string
-	ShellArg        string
-	OpenCommand     string
-	OpenLinkCommand string
+	OS                  string
+	Shell               string
+	InteractiveShell    string
+	ShellArg            string
+	InteractiveShellArg string
+	OpenCommand         string
+	OpenLinkCommand     string
 }
 
 // NewOSCommand os command runner
diff --git a/pkg/commands/oscommands/os_default_platform.go b/pkg/commands/oscommands/os_default_platform.go
index fd4967d9576..196e4d9f634 100644
--- a/pkg/commands/oscommands/os_default_platform.go
+++ b/pkg/commands/oscommands/os_default_platform.go
@@ -4,15 +4,26 @@
 package oscommands
 
 import (
+	"os"
 	"runtime"
 )
 
 func GetPlatform() *Platform {
 	return &Platform{
-		OS:              runtime.GOOS,
-		Shell:           "bash",
-		ShellArg:        "-c",
-		OpenCommand:     "open {{filename}}",
-		OpenLinkCommand: "open {{link}}",
+		OS:                  runtime.GOOS,
+		Shell:               "bash",
+		InteractiveShell:    getUserShell(),
+		ShellArg:            "-c",
+		InteractiveShellArg: "-i",
+		OpenCommand:         "open {{filename}}",
+		OpenLinkCommand:     "open {{link}}",
 	}
 }
+
+func getUserShell() string {
+	if shell := os.Getenv("SHELL"); shell != "" {
+		return shell
+	}
+
+	return "bash"
+}
diff --git a/pkg/commands/oscommands/os_windows.go b/pkg/commands/oscommands/os_windows.go
index 783f50518a1..32cd59edb0a 100644
--- a/pkg/commands/oscommands/os_windows.go
+++ b/pkg/commands/oscommands/os_windows.go
@@ -2,8 +2,10 @@ package oscommands
 
 func GetPlatform() *Platform {
 	return &Platform{
-		OS:       "windows",
-		Shell:    "cmd",
-		ShellArg: "/c",
+		OS:                  "windows",
+		Shell:               "cmd",
+		InteractiveShell:    "cmd",
+		ShellArg:            "/c",
+		InteractiveShellArg: "",
 	}
 }
diff --git a/pkg/gui/controllers/custom_command_action.go b/pkg/gui/controllers/custom_command_action.go
index 39777e70a0d..7b8cbc641bf 100644
--- a/pkg/gui/controllers/custom_command_action.go
+++ b/pkg/gui/controllers/custom_command_action.go
@@ -31,7 +31,7 @@ func (self *CustomCommandAction) Call() error {
 
 			self.c.LogAction(self.c.Tr.Actions.CustomCommand)
 			return self.c.RunSubprocessAndRefresh(
-				self.c.OS().Cmd.NewShell(command),
+				self.c.OS().Cmd.NewInteractiveShell(command),
 			)
 		},
 		HandleDeleteSuggestion: func(index int) error {


Test patch: