1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2024-12-14 11:23:09 +02:00
lazygit/pkg/integration/components/shell.go

312 lines
8.2 KiB
Go
Raw Normal View History

2022-08-12 01:19:39 +02:00
package components
2022-08-07 14:09:39 +02:00
import (
"fmt"
"io"
2022-08-07 14:09:39 +02:00
"os"
"path/filepath"
"runtime"
2022-08-07 14:09:39 +02:00
"github.com/jesseduffield/lazygit/pkg/secureexec"
)
// this is for running shell commands, mostly for the sake of setting up the repo
// but you can also run the commands from within lazygit to emulate things happening
// in the background.
type Shell struct {
// working directory the shell is invoked in
dir string
2022-12-27 12:47:37 +02:00
// when running the shell outside the gui we can directly panic on failure,
// but inside the gui we need to close the gui before panicking
fail func(string)
}
2022-08-07 14:09:39 +02:00
2022-12-27 12:47:37 +02:00
func NewShell(dir string, fail func(string)) *Shell {
return &Shell{dir: dir, fail: fail}
}
2022-08-07 14:09:39 +02:00
func (self *Shell) RunCommand(args []string) *Shell {
output, err := self.runCommandWithOutput(args)
2022-08-07 14:09:39 +02:00
if err != nil {
self.fail(fmt.Sprintf("error running command: %v\n%s", args, output))
2022-08-07 14:09:39 +02:00
}
2022-12-27 12:47:37 +02:00
return self
2022-08-07 14:09:39 +02:00
}
func (self *Shell) RunCommandExpectError(args []string) *Shell {
output, err := self.runCommandWithOutput(args)
if err == nil {
self.fail(fmt.Sprintf("Expected error running shell command: %v\n%s", args, output))
}
2023-02-26 02:49:15 +02:00
return self
}
func (self *Shell) runCommandWithOutput(args []string) (string, error) {
2022-12-27 13:52:20 +02:00
cmd := secureexec.Command(args[0], args[1:]...)
cmd.Env = os.Environ()
cmd.Dir = self.dir
output, err := cmd.CombinedOutput()
return string(output), err
}
2022-12-27 12:47:37 +02:00
func (self *Shell) RunShellCommand(cmdStr string) *Shell {
shell := "sh"
shellArg := "-c"
if runtime.GOOS == "windows" {
shell = "cmd"
shellArg = "/C"
2022-12-24 07:46:01 +02:00
}
cmd := secureexec.Command(shell, shellArg, cmdStr)
2022-12-24 07:46:01 +02:00
cmd.Env = os.Environ()
2022-12-27 12:47:37 +02:00
cmd.Dir = self.dir
2022-12-24 07:46:01 +02:00
output, err := cmd.CombinedOutput()
if err != nil {
self.fail(fmt.Sprintf("error running shell command: %s\n%s", cmdStr, string(output)))
2022-12-24 07:46:01 +02:00
}
2022-12-27 12:47:37 +02:00
return self
2022-12-24 07:46:01 +02:00
}
2022-12-27 12:47:37 +02:00
func (self *Shell) CreateFile(path string, content string) *Shell {
fullPath := filepath.Join(self.dir, path)
2022-09-13 12:11:03 +02:00
err := os.WriteFile(fullPath, []byte(content), 0o644)
2022-08-07 14:09:39 +02:00
if err != nil {
2022-12-27 12:47:37 +02:00
self.fail(fmt.Sprintf("error creating file: %s\n%s", fullPath, err))
2022-08-07 14:09:39 +02:00
}
2022-12-27 12:47:37 +02:00
return self
2022-08-07 14:09:39 +02:00
}
func (self *Shell) DeleteFile(path string) *Shell {
fullPath := filepath.Join(self.dir, path)
err := os.Remove(fullPath)
if err != nil {
self.fail(fmt.Sprintf("error deleting file: %s\n%s", fullPath, err))
}
return self
}
2022-12-27 12:47:37 +02:00
func (self *Shell) CreateDir(path string) *Shell {
fullPath := filepath.Join(self.dir, path)
2022-11-11 03:16:38 +02:00
if err := os.MkdirAll(fullPath, 0o755); err != nil {
2022-12-27 12:47:37 +02:00
self.fail(fmt.Sprintf("error creating directory: %s\n%s", fullPath, err))
2022-11-11 03:16:38 +02:00
}
2022-12-27 12:47:37 +02:00
return self
2022-11-11 03:16:38 +02:00
}
2022-12-27 12:47:37 +02:00
func (self *Shell) UpdateFile(path string, content string) *Shell {
fullPath := filepath.Join(self.dir, path)
2022-09-13 12:11:03 +02:00
err := os.WriteFile(fullPath, []byte(content), 0o644)
2022-08-22 12:43:19 +02:00
if err != nil {
2022-12-27 12:47:37 +02:00
self.fail(fmt.Sprintf("error updating file: %s\n%s", fullPath, err))
2022-08-22 12:43:19 +02:00
}
2022-12-27 12:47:37 +02:00
return self
2022-08-22 12:43:19 +02:00
}
2022-12-27 12:47:37 +02:00
func (self *Shell) NewBranch(name string) *Shell {
return self.RunCommand([]string{"git", "checkout", "-b", name})
2022-08-07 14:09:39 +02:00
}
2022-12-27 12:47:37 +02:00
func (self *Shell) Checkout(name string) *Shell {
return self.RunCommand([]string{"git", "checkout", name})
2022-08-22 11:52:05 +02:00
}
2022-12-27 12:47:37 +02:00
func (self *Shell) Merge(name string) *Shell {
return self.RunCommand([]string{"git", "merge", "--commit", "--no-ff", name})
}
2023-02-19 05:47:07 +02:00
func (self *Shell) ContinueMerge() *Shell {
return self.RunCommand([]string{"git", "-c", "core.editor=true", "merge", "--continue"})
2023-02-19 05:47:07 +02:00
}
2022-12-27 12:47:37 +02:00
func (self *Shell) GitAdd(path string) *Shell {
return self.RunCommand([]string{"git", "add", path})
2022-08-08 13:32:58 +02:00
}
2022-12-27 12:47:37 +02:00
func (self *Shell) GitAddAll() *Shell {
return self.RunCommand([]string{"git", "add", "-A"})
2022-08-07 14:09:39 +02:00
}
2022-12-27 12:47:37 +02:00
func (self *Shell) Commit(message string) *Shell {
return self.RunCommand([]string{"git", "commit", "-m", message})
2022-08-07 14:09:39 +02:00
}
2022-12-27 12:47:37 +02:00
func (self *Shell) EmptyCommit(message string) *Shell {
return self.RunCommand([]string{"git", "commit", "--allow-empty", "-m", message})
2022-08-07 14:09:39 +02:00
}
2022-08-08 13:32:58 +02:00
2023-02-25 04:08:45 +02:00
func (self *Shell) Revert(ref string) *Shell {
return self.RunCommand([]string{"git", "revert", ref})
2023-02-25 04:08:45 +02:00
}
2023-02-20 09:45:14 +02:00
func (self *Shell) CreateLightweightTag(name string, ref string) *Shell {
return self.RunCommand([]string{"git", "tag", name, ref})
2023-02-20 09:45:14 +02:00
}
2023-02-21 12:49:48 +02:00
func (self *Shell) CreateAnnotatedTag(name string, message string, ref string) *Shell {
return self.RunCommand([]string{"git", "tag", "-a", name, "-m", message, ref})
2023-02-21 12:49:48 +02:00
}
// convenience method for creating a file and adding it
2022-12-27 12:47:37 +02:00
func (self *Shell) CreateFileAndAdd(fileName string, fileContents string) *Shell {
return self.
2022-08-08 13:32:58 +02:00
CreateFile(fileName, fileContents).
GitAdd(fileName)
}
2022-08-22 12:43:19 +02:00
// convenience method for updating a file and adding it
2022-12-27 12:47:37 +02:00
func (self *Shell) UpdateFileAndAdd(fileName string, fileContents string) *Shell {
return self.
2022-08-22 12:43:19 +02:00
UpdateFile(fileName, fileContents).
GitAdd(fileName)
}
// convenience method for deleting a file and adding it
func (self *Shell) DeleteFileAndAdd(fileName string) *Shell {
return self.
DeleteFile(fileName).
GitAdd(fileName)
}
// creates commits 01, 02, 03, ..., n with a new file in each
// The reason for padding with zeroes is so that it's easier to do string
// matches on the commit messages when there are many of them
2022-12-27 12:47:37 +02:00
func (self *Shell) CreateNCommits(n int) *Shell {
return self.CreateNCommitsStartingAt(n, 1)
}
func (self *Shell) CreateNCommitsStartingAt(n, startIndex int) *Shell {
for i := startIndex; i < startIndex+n; i++ {
2022-12-27 12:47:37 +02:00
self.CreateFileAndAdd(
2022-08-08 13:32:58 +02:00
fmt.Sprintf("file%02d.txt", i),
fmt.Sprintf("file%02d content", i),
).
Commit(fmt.Sprintf("commit %02d", i))
}
2022-12-27 12:47:37 +02:00
return self
2022-08-08 13:32:58 +02:00
}
2022-12-27 12:47:37 +02:00
func (self *Shell) StashWithMessage(message string) *Shell {
self.RunCommand([]string{"git", "stash", "-m", message})
2022-12-27 12:47:37 +02:00
return self
}
2022-12-27 12:47:37 +02:00
func (self *Shell) SetConfig(key string, value string) *Shell {
self.RunCommand([]string{"git", "config", "--local", key, value})
2022-12-27 12:47:37 +02:00
return self
}
2022-12-28 08:39:45 +02:00
func (self *Shell) CloneIntoRemote(name string) *Shell {
2023-02-26 02:49:15 +02:00
self.Clone(name)
self.RunCommand([]string{"git", "remote", "add", name, "../" + name})
self.RunCommand([]string{"git", "fetch", name})
2022-12-28 08:39:45 +02:00
return self
}
2023-02-26 02:49:15 +02:00
func (self *Shell) CloneIntoSubmodule(submoduleName string) *Shell {
self.Clone("other_repo")
self.RunCommand([]string{"git", "submodule", "add", "../other_repo", submoduleName})
2023-02-26 02:49:15 +02:00
return self
}
func (self *Shell) Clone(repoName string) *Shell {
self.RunCommand([]string{"git", "clone", "--bare", ".", "../" + repoName})
2023-02-26 02:49:15 +02:00
return self
}
2022-12-28 08:39:45 +02:00
func (self *Shell) SetBranchUpstream(branch string, upstream string) *Shell {
self.RunCommand([]string{"git", "branch", "--set-upstream-to=" + upstream, branch})
2022-12-28 08:39:45 +02:00
return self
}
func (self *Shell) RemoveRemoteBranch(remoteName string, branch string) *Shell {
self.RunCommand([]string{"git", "-C", "../" + remoteName, "branch", "-d", branch})
2022-12-28 08:39:45 +02:00
return self
}
func (self *Shell) HardReset(ref string) *Shell {
self.RunCommand([]string{"git", "reset", "--hard", ref})
return self
}
2023-02-20 12:52:27 +02:00
func (self *Shell) Stash(message string) *Shell {
self.RunCommand([]string{"git", "stash", "-m", message})
return self
}
func (self *Shell) StartBisect(good string, bad string) *Shell {
self.RunCommand([]string{"git", "bisect", "start", good, bad})
return self
}
func (self *Shell) Init() *Shell {
self.RunCommand([]string{"git", "init", "-b", "master"})
return self
}
func (self *Shell) MakeExecutable(path string) *Shell {
// 0755 sets the executable permission for owner, and read/execute permissions for group and others
err := os.Chmod(filepath.Join(self.dir, path), 0o755)
if err != nil {
panic(err)
}
return self
}
// Help files are located at test/files from the root the lazygit repo.
// E.g. You may want to create a pre-commit hook file there, then call this
// function to copy it into your test repo.
func (self *Shell) CopyHelpFile(source string, destination string) *Shell {
return self.CopyFile(fmt.Sprintf("../../../../../files/%s", source), destination)
}
func (self *Shell) CopyFile(source string, destination string) *Shell {
absSourcePath := filepath.Join(self.dir, source)
absDestPath := filepath.Join(self.dir, destination)
sourceFile, err := os.Open(absSourcePath)
if err != nil {
self.fail(err.Error())
}
defer sourceFile.Close()
destinationFile, err := os.Create(absDestPath)
if err != nil {
self.fail(err.Error())
}
defer destinationFile.Close()
_, err = io.Copy(destinationFile, sourceFile)
if err != nil {
self.fail(err.Error())
}
// copy permissions to destination file too
sourceFileInfo, err := os.Stat(absSourcePath)
if err != nil {
self.fail(err.Error())
}
err = os.Chmod(absDestPath, sourceFileInfo.Mode())
if err != nil {
self.fail(err.Error())
}
2023-02-20 12:52:27 +02:00
return self
}