diff options
author | Jesse Duffield <jessedduffield@gmail.com> | 2022-08-14 14:33:44 +1000 |
---|---|---|
committer | Jesse Duffield <jessedduffield@gmail.com> | 2022-08-14 17:20:52 +1000 |
commit | 5173d7f5e17fbcab8cf2411abae1e064b076ebbb (patch) | |
tree | c5cc281b56f6b927e5408ec1961bfafd4dea8049 /pkg | |
parent | 349a7d24532a32feaaf40129b3b07b9215b8a625 (diff) |
better CLI interface
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/cheatsheet/generate.go | 4 | ||||
-rw-r--r-- | pkg/integration/README.md | 10 | ||||
-rw-r--r-- | pkg/integration/clients/cli.go (renamed from pkg/integration/cmd/runner/main.go) | 39 | ||||
-rw-r--r-- | pkg/integration/clients/go_test.go (renamed from pkg/integration/go_test.go) | 13 | ||||
-rw-r--r-- | pkg/integration/clients/injector/main.go (renamed from pkg/integration/cmd/injector/main.go) | 13 | ||||
-rw-r--r-- | pkg/integration/clients/tui.go (renamed from pkg/integration/cmd/tui/main.go) | 205 | ||||
-rw-r--r-- | pkg/integration/components/runner.go | 51 | ||||
-rw-r--r-- | pkg/integration/components/snapshot.go | 4 | ||||
-rw-r--r-- | pkg/integration/deprecated/cmd/runner/main.go | 2 | ||||
-rw-r--r-- | pkg/utils/utils.go | 27 |
10 files changed, 233 insertions, 135 deletions
diff --git a/pkg/cheatsheet/generate.go b/pkg/cheatsheet/generate.go index 73ae07a55..3b9d9c2d1 100644 --- a/pkg/cheatsheet/generate.go +++ b/pkg/cheatsheet/generate.go @@ -20,7 +20,7 @@ import ( "github.com/jesseduffield/lazygit/pkg/gui/keybindings" "github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/i18n" - "github.com/jesseduffield/lazygit/pkg/integration" + "github.com/jesseduffield/lazygit/pkg/utils" "github.com/samber/lo" ) @@ -45,7 +45,7 @@ func CommandToRun() string { } func GetDir() string { - return integration.GetProjectRootDirectory() + "/docs/keybindings" + return utils.GetLazygitRootDirectory() + "/docs/keybindings" } func generateAtDir(cheatsheetDir string) { diff --git a/pkg/integration/README.md b/pkg/integration/README.md index 1bc9d0a85..ba2365403 100644 --- a/pkg/integration/README.md +++ b/pkg/integration/README.md @@ -37,21 +37,21 @@ If you find yourself doing something frequently in a test, consider making it a There are three ways to invoke a test: -1. go run pkg/integration/cmd/runner/main.go [<testname>...] -2. go run pkg/integration/cmd/tui/main.go -3. go test pkg/integration/go_test.go +1. go run cmd/integration_test/main.go cli [<testname>...] +2. go run cmd/integration_test/main.go tui +3. go test pkg/integration/clients/go_test.go The first, the test runner, is for directly running a test from the command line. If you pass no arguments, it runs all tests. The second, the TUI, is for running tests from a terminal UI where it's easier to find a test and run it without having to copy it's name and paste it into the terminal. This is the easiest approach by far. The third, the go-test command, intended only for use in CI, to be run along with the other `go test` tests. This runs the tests in headless mode so there's no visual output. -The name of a test is based on its path, so the name of the test at `pkg/integration/tests/commit/new_branch.go` is commit/new_branch. So to run it with our test runner you would run `go run pkg/integration/cmd/runner/main.go commit/new_branch`. +The name of a test is based on its path, so the name of the test at `pkg/integration/tests/commit/new_branch.go` is commit/new_branch. So to run it with our test runner you would run `go run cmd/integration_test/main.go cli commit/new_branch`. You can pass the KEY_PRESS_DELAY env var to the test runner in order to set a delay in milliseconds between keypresses, which helps for watching a test at a realistic speed to understand what it's doing. Or in the tui you can press 't' to run the test with a pre-set delay. ### Snapshots -At the moment (this is subject to change) each test has a snapshot repo created after running for the first time. These snapshots live in `test/integration_new`, in folders named 'expected' (alongside the 'actual' folders which contain the resulting repo from the last test run). Whenever you run a test, the resultant repo will be compared against the snapshot repo and if they're different, you'll be asked whether you want to update the snapshot. If you want to update a snapshot without being prompted you can pass MODE=updateSnapshot to the test runner. +At the moment (this is subject to change) each test has a snapshot repo created after running for the first time. These snapshots live in `test/integration_new`, in folders named 'expected' (alongside the 'actual' folders which contain the resulting repo from the last test run). Whenever you run a test, the resultant repo will be compared against the snapshot repo and if they're different, you'll be asked whether you want to update the snapshot. If you want to update a snapshot without being prompted you can pass MODE=update to the test runner. ### Sandbox mode diff --git a/pkg/integration/cmd/runner/main.go b/pkg/integration/clients/cli.go index ba8bc9d75..76f0c9549 100644 --- a/pkg/integration/cmd/runner/main.go +++ b/pkg/integration/clients/cli.go @@ -1,9 +1,10 @@ -package main +package clients import ( "log" "os" "os/exec" + "strconv" "github.com/jesseduffield/lazygit/pkg/integration/components" "github.com/jesseduffield/lazygit/pkg/integration/tests" @@ -19,32 +20,35 @@ import ( // If invoked directly, you can specify tests to run by passing their names as positional arguments -func main() { +func RunCLI(testNames []string) { err := components.RunTests( - getTestsToRun(), + getTestsToRun(testNames), log.Printf, runCmdInTerminal, - func(test *components.IntegrationTest, f func() error) { - if err := f(); err != nil { - log.Print(err.Error()) - } - }, + runAndPrintError, getModeFromEnv(), + tryConvert(os.Getenv("KEY_PRESS_DELAY"), 0), ) if err != nil { log.Print(err.Error()) } } -func getTestsToRun() []*components.IntegrationTest { +func runAndPrintError(test *components.IntegrationTest, f func() error) { + if err := f(); err != nil { + log.Print(err.Error()) + } +} + +func getTestsToRun(testNames []string) []*components.IntegrationTest { var testsToRun []*components.IntegrationTest - if len(os.Args) < 2 { + if len(testNames) == 0 { return tests.Tests } outer: - for _, testName := range os.Args[1:] { + for _, testName := range testNames { // check if our given test name actually exists for _, test := range tests.Tests { if test.Name() == testName { @@ -72,12 +76,21 @@ func getModeFromEnv() components.Mode { return components.ASK_TO_UPDATE_SNAPSHOT case "check": return components.CHECK_SNAPSHOT - case "updateSnapshot": + case "update": return components.UPDATE_SNAPSHOT case "sandbox": return components.SANDBOX default: - log.Fatalf("unknown test mode: %s, must be one of [test, record, updateSnapshot, sandbox]", os.Getenv("MODE")) + log.Fatalf("unknown test mode: %s, must be one of [ask, check, update, sandbox]", os.Getenv("MODE")) panic("unreachable") } } + +func tryConvert(numStr string, defaultVal int) int { + num, err := strconv.Atoi(numStr) + if err != nil { + return defaultVal + } + + return num +} diff --git a/pkg/integration/go_test.go b/pkg/integration/clients/go_test.go index 3dd03ec56..d52cd409a 100644 --- a/pkg/integration/go_test.go +++ b/pkg/integration/clients/go_test.go @@ -1,7 +1,7 @@ //go:build !windows // +build !windows -package integration +package clients // this is the new way of running tests. See pkg/integration/integration_tests/commit.go // for an example @@ -11,7 +11,6 @@ import ( "io/ioutil" "os" "os/exec" - "strconv" "testing" "github.com/creack/pty" @@ -45,6 +44,7 @@ func TestIntegration(t *testing.T) { }) }, components.CHECK_SNAPSHOT, + 0, ) assert.NoError(t, err) @@ -66,12 +66,3 @@ func runCmdHeadless(cmd *exec.Cmd) error { return f.Close() } - -func tryConvert(numStr string, defaultVal int) int { - num, err := strconv.Atoi(numStr) - if err != nil { - return defaultVal - } - - return num -} diff --git a/pkg/integration/cmd/injector/main.go b/pkg/integration/clients/injector/main.go index ceb770065..263dba5da 100644 --- a/pkg/integration/cmd/injector/main.go +++ b/pkg/integration/clients/injector/main.go @@ -12,12 +12,12 @@ import ( ) // The purpose of this program is to run lazygit with an integration test passed in. -// We could have done the check on LAZYGIT_TEST_NAME in the root main.go but +// We could have done the check on TEST_NAME in the root main.go but // that would mean lazygit would be depending on integration test code which // would bloat the binary. // You should not invoke this program directly. Instead you should go through -// pkg/integration/cmd/runner/main.go or pkg/integration/cmd/tui/main.go +// go run cmd/integration_test/main.go func main() { dummyBuildInfo := &app.BuildInfo{ @@ -39,11 +39,16 @@ func getIntegrationTest() integrationTypes.IntegrationTest { return nil } - integrationTestName := os.Getenv(components.LAZYGIT_TEST_NAME_ENV_VAR) + if os.Getenv(components.SANDBOX_ENV_VAR) == "true" { + // when in sandbox mode we don't want the test controlling the gui + return nil + } + + integrationTestName := os.Getenv(components.TEST_NAME_ENV_VAR) if integrationTestName == "" { panic(fmt.Sprintf( "expected %s environment variable to be set, given that we're running an integration test", - components.LAZYGIT_TEST_NAME_ENV_VAR, + components.TEST_NAME_ENV_VAR, )) } diff --git a/pkg/integration/cmd/tui/main.go b/pkg/integration/clients/tui.go index cabd55210..707e482ca 100644 --- a/pkg/integration/cmd/tui/main.go +++ b/pkg/integration/clients/tui.go @@ -1,49 +1,29 @@ -package main +package clients import ( "fmt" "log" "os" - "os/exec" "path/filepath" + "strings" + "github.com/jesseduffield/generics/slices" "github.com/jesseduffield/gocui" "github.com/jesseduffield/lazygit/pkg/gui" "github.com/jesseduffield/lazygit/pkg/gui/style" "github.com/jesseduffield/lazygit/pkg/integration/components" "github.com/jesseduffield/lazygit/pkg/integration/tests" "github.com/jesseduffield/lazygit/pkg/secureexec" + "github.com/jesseduffield/lazygit/pkg/utils" ) // This program lets you run integration tests from a TUI. See pkg/integration/README.md for more info. -type App struct { - tests []*components.IntegrationTest - itemIdx int - testDir string - filtering bool - g *gocui.Gui -} - -func (app *App) getCurrentTest() *components.IntegrationTest { - if len(app.tests) > 0 { - return app.tests[app.itemIdx] - } - return nil -} - -func (app *App) loadTests() { - app.tests = tests.Tests - if app.itemIdx > len(app.tests)-1 { - app.itemIdx = len(app.tests) - 1 - } -} - -func main() { - rootDir := components.GetProjectRootDirectory() +func RunTUI() { + rootDir := utils.GetLazygitRootDirectory() testDir := filepath.Join(rootDir, "test", "integration") - app := &App{testDir: testDir} + app := newApp(testDir) app.loadTests() g, err := gocui.NewGui(gocui.OutputTrue, false, gocui.NORMAL, false, gui.RuneReplacements) @@ -71,6 +51,21 @@ func main() { log.Panicln(err) } + if err := g.SetKeybinding("list", gocui.KeyArrowDown, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { + if app.itemIdx < len(app.filteredTests)-1 { + app.itemIdx++ + } + + listView, err := g.View("list") + if err != nil { + return err + } + listView.FocusPoint(0, app.itemIdx) + return nil + }); err != nil { + log.Panicln(err) + } + if err := g.SetKeybinding("list", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { log.Panicln(err) } @@ -85,8 +80,7 @@ func main() { return nil } - cmd := secureexec.Command("sh", "-c", fmt.Sprintf("MODE=sandbox go run pkg/integration/cmd/runner/main.go %s", currentTest.Name())) - app.runSubprocess(cmd) + suspendAndRunTest(currentTest, components.SANDBOX, 0) return nil }); err != nil { @@ -99,8 +93,7 @@ func main() { return nil } - cmd := secureexec.Command("sh", "-c", fmt.Sprintf("go run pkg/integration/cmd/runner/main.go %s", currentTest.Name())) - app.runSubprocess(cmd) + suspendAndRunTest(currentTest, components.ASK_TO_UPDATE_SNAPSHOT, 0) return nil }); err != nil { @@ -113,8 +106,7 @@ func main() { return nil } - cmd := secureexec.Command("sh", "-c", fmt.Sprintf("KEY_PRESS_DELAY=200 go run pkg/integration/cmd/runner/main.go %s", currentTest.Name())) - app.runSubprocess(cmd) + suspendAndRunTest(currentTest, components.ASK_TO_UPDATE_SNAPSHOT, 200) return nil }); err != nil { @@ -176,6 +168,26 @@ func main() { return err } + app.filteredTests = tests.Tests + app.renderTests() + app.editorView.TextArea.Clear() + app.editorView.Clear() + app.editorView.Reset() + + return nil + }); err != nil { + log.Panicln(err) + } + + if err := g.SetKeybinding("editor", gocui.KeyEnter, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { + app.filtering = false + + if _, err := g.SetCurrentView("list"); err != nil { + return err + } + + app.renderTests() + return nil }); err != nil { log.Panicln(err) @@ -191,20 +203,74 @@ func main() { } } -func (app *App) runSubprocess(cmd *exec.Cmd) { +type app struct { + filteredTests []*components.IntegrationTest + itemIdx int + testDir string + filtering bool + g *gocui.Gui + listView *gocui.View + editorView *gocui.View +} + +func newApp(testDir string) *app { + return &app{testDir: testDir} +} + +func (self *app) getCurrentTest() *components.IntegrationTest { + self.adjustCursor() + if len(self.filteredTests) > 0 { + return self.filteredTests[self.itemIdx] + } + return nil +} + +func (self *app) loadTests() { + self.filteredTests = tests.Tests + + self.adjustCursor() +} + +func (self *app) adjustCursor() { + self.itemIdx = utils.Clamp(self.itemIdx, 0, len(self.filteredTests)-1) +} + +func (self *app) filterWithString(needle string) { + if needle == "" { + self.filteredTests = tests.Tests + } else { + self.filteredTests = slices.Filter(tests.Tests, func(test *components.IntegrationTest) bool { + return strings.Contains(test.Name(), needle) + }) + } + + self.renderTests() + self.g.Update(func(g *gocui.Gui) error { return nil }) +} + +func (self *app) renderTests() { + self.listView.Clear() + for _, test := range self.filteredTests { + fmt.Fprintln(self.listView, test.Name()) + } +} + +func (self *app) wrapEditor(f func(v *gocui.View, key gocui.Key, ch rune, mod gocui.Modifier) bool) func(v *gocui.View, key gocui.Key, ch rune, mod gocui.Modifier) bool { + return func(v *gocui.View, key gocui.Key, ch rune, mod gocui.Modifier) bool { + matched := f(v, key, ch, mod) + if matched { + self.filterWithString(v.TextArea.GetContent()) + } + return matched + } +} + +func suspendAndRunTest(test *components.IntegrationTest, mode components.Mode, keyPressDelay int) { if err := gocui.Screen.Suspend(); err != nil { panic(err) } - cmd.Stdin = os.Stdin - cmd.Stderr = os.Stderr - cmd.Stdout = os.Stdout - if err := cmd.Run(); err != nil { - log.Println(err.Error()) - } - cmd.Stdin = nil - cmd.Stderr = nil - cmd.Stdout = nil + runTuiTest(test, mode, keyPressDelay) fmt.Fprintf(os.Stdout, "\n%s", style.FgGreen.Sprint("press enter to return")) fmt.Scanln() // wait for enter press @@ -214,29 +280,31 @@ func (app *App) runSubprocess(cmd *exec.Cmd) { } } -func (app *App) layout(g *gocui.Gui) error { +func (self *app) layout(g *gocui.Gui) error { maxX, maxY := g.Size() descriptionViewHeight := 7 keybindingsViewHeight := 3 editorViewHeight := 3 - if !app.filtering { + if !self.filtering { editorViewHeight = 0 } else { descriptionViewHeight = 0 keybindingsViewHeight = 0 } - g.Cursor = app.filtering + g.Cursor = self.filtering g.FgColor = gocui.ColorGreen listView, err := g.SetView("list", 0, 0, maxX-1, maxY-descriptionViewHeight-keybindingsViewHeight-editorViewHeight-1, 0) if err != nil { if err.Error() != "unknown view" { return err } - listView.Highlight = true - listView.Clear() - for _, test := range app.tests { - fmt.Fprintln(listView, test.Name()) + + if self.listView == nil { + self.listView = listView } + + listView.Highlight = true + self.renderTests() listView.Title = "Tests" listView.FgColor = gocui.ColorDefault if _, err := g.SetCurrentView("list"); err != nil { @@ -270,12 +338,18 @@ func (app *App) layout(g *gocui.Gui) error { if err.Error() != "unknown view" { return err } + + if self.editorView == nil { + self.editorView = editorView + } + editorView.Title = "Filter" editorView.FgColor = gocui.ColorDefault editorView.Editable = true + editorView.Editor = gocui.EditorFunc(self.wrapEditor(gocui.SimpleEditor)) } - currentTest := app.getCurrentTest() + currentTest := self.getCurrentTest() if currentTest == nil { return nil } @@ -283,24 +357,23 @@ func (app *App) layout(g *gocui.Gui) error { descriptionView.Clear() fmt.Fprint(descriptionView, currentTest.Description()) - if err := g.SetKeybinding("list", gocui.KeyArrowDown, gocui.ModNone, func(*gocui.Gui, *gocui.View) error { - if app.itemIdx < len(app.tests)-1 { - app.itemIdx++ - } - - listView, err := g.View("list") - if err != nil { - return err - } - listView.FocusPoint(0, app.itemIdx) - return nil - }); err != nil { - log.Panicln(err) - } - return nil } func quit(g *gocui.Gui, v *gocui.View) error { return gocui.ErrQuit } + +func runTuiTest(test *components.IntegrationTest, mode components.Mode, keyPressDelay int) { + err := components.RunTests( + []*components.IntegrationTest{test}, + log.Printf, + runCmdInTerminal, + runAndPrintError, + mode, + keyPressDelay, + ) + if err != nil { + log.Println(err.Error()) + } +} diff --git a/pkg/integration/components/runner.go b/pkg/integration/components/runner.go index 1b7fb0a36..5a5022c53 100644 --- a/pkg/integration/components/runner.go +++ b/pkg/integration/components/runner.go @@ -3,17 +3,20 @@ package components import ( "fmt" "io/ioutil" - "log" "os" "os/exec" "path/filepath" "github.com/jesseduffield/lazygit/pkg/commands/oscommands" + "github.com/jesseduffield/lazygit/pkg/utils" ) // this is the integration runner for the new and improved integration interface -const LAZYGIT_TEST_NAME_ENV_VAR = "LAZYGIT_TEST_NAME" +const ( + TEST_NAME_ENV_VAR = "TEST_NAME" + SANDBOX_ENV_VAR = "SANDBOX" +) type Mode int @@ -38,8 +41,9 @@ func RunTests( runCmd func(cmd *exec.Cmd) error, testWrapper func(test *IntegrationTest, f func() error), mode Mode, + keyPressDelay int, ) error { - projectRootDir := GetProjectRootDirectory() + projectRootDir := utils.GetLazygitRootDirectory() err := os.Chdir(projectRootDir) if err != nil { return err @@ -59,7 +63,7 @@ func RunTests( ) testWrapper(test, func() error { //nolint: thelper - return runTest(test, paths, projectRootDir, logf, runCmd, mode) + return runTest(test, paths, projectRootDir, logf, runCmd, mode, keyPressDelay) }) } @@ -73,6 +77,7 @@ func runTest( logf func(format string, formatArgs ...interface{}), runCmd func(cmd *exec.Cmd) error, mode Mode, + keyPressDelay int, ) error { if test.Skip() { logf("Skipping test %s", test.Name()) @@ -85,7 +90,7 @@ func runTest( return err } - cmd, err := getLazygitCommand(test, paths, projectRootDir) + cmd, err := getLazygitCommand(test, paths, projectRootDir, mode, keyPressDelay) if err != nil { return err } @@ -116,7 +121,7 @@ func prepareTestDir( func buildLazygit() error { osCommand := oscommands.NewDummyOSCommand() return osCommand.Cmd.New(fmt.Sprintf( - "go build -o %s pkg/integration/cmd/injector/main.go", tempLazygitPath(), + "go build -o %s pkg/integration/clients/injector/main.go", tempLazygitPath(), )).Run() } @@ -144,7 +149,7 @@ func createFixture(test *IntegrationTest, paths Paths) error { return nil } -func getLazygitCommand(test *IntegrationTest, paths Paths, rootDir string) (*exec.Cmd, error) { +func getLazygitCommand(test *IntegrationTest, paths Paths, rootDir string, mode Mode, keyPressDelay int) (*exec.Cmd, error) { osCommand := oscommands.NewDummyOSCommand() templateConfigDir := filepath.Join(rootDir, "test", "default_test_config") @@ -162,34 +167,16 @@ func getLazygitCommand(test *IntegrationTest, paths Paths, rootDir string) (*exe cmdObj := osCommand.Cmd.New(cmdStr) - cmdObj.AddEnvVars(fmt.Sprintf("%s=%s", LAZYGIT_TEST_NAME_ENV_VAR, test.Name())) - - return cmdObj.GetCmd(), nil -} - -func GetProjectRootDirectory() string { - path, err := os.Getwd() - if err != nil { - panic(err) + cmdObj.AddEnvVars(fmt.Sprintf("%s=%s", TEST_NAME_ENV_VAR, test.Name())) + if mode == SANDBOX { + cmdObj.AddEnvVars(fmt.Sprintf("%s=%s", "SANDBOX", "true")) } - for { - _, err := os.Stat(filepath.Join(path, ".git")) - - if err == nil { - return path - } - - if !os.IsNotExist(err) { - panic(err) - } - - path = filepath.Dir(path) - - if path == "/" { - log.Fatal("must run in lazygit folder or child folder") - } + if keyPressDelay > 0 { + cmdObj.AddEnvVars(fmt.Sprintf("KEY_PRESS_DELAY=%d", keyPressDelay)) } + + return cmdObj.GetCmd(), nil } func tempLazygitPath() string { diff --git a/pkg/integration/components/snapshot.go b/pkg/integration/components/snapshot.go index 8c87e6727..b7efa0fb0 100644 --- a/pkg/integration/components/snapshot.go +++ b/pkg/integration/components/snapshot.go @@ -54,7 +54,7 @@ func (self *Snapshotter) handleSnapshots() error { case ASK_TO_UPDATE_SNAPSHOT: return self.handleAskToUpdate() case SANDBOX: - self.logf("Session exited") + self.logf("Sandbox session exited") } return nil } @@ -68,6 +68,7 @@ func (self *Snapshotter) handleUpdate() error { } func (self *Snapshotter) handleCheck() error { + self.logf("Comparing snapshots") if err := self.compareSnapshots(); err != nil { return err } @@ -85,6 +86,7 @@ func (self *Snapshotter) handleAskToUpdate() error { return nil } + self.logf("Comparing snapshots...") if err := self.compareSnapshots(); err != nil { self.logf("%s", err) diff --git a/pkg/integration/deprecated/cmd/runner/main.go b/pkg/integration/deprecated/cmd/runner/main.go index e225b3bd3..86f3c1f14 100644 --- a/pkg/integration/deprecated/cmd/runner/main.go +++ b/pkg/integration/deprecated/cmd/runner/main.go @@ -11,7 +11,7 @@ import ( "github.com/stretchr/testify/assert" ) -// Deprecated: This file is part of the old way of doing things. See pkg/integration/cmd/runner/main.go for the new way +// Deprecated: This file is part of the old way of doing things. // see https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md // This file can be invoked directly, but you might find it easier to go through diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 2f33862e8..9d6213c1d 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -135,3 +135,30 @@ func FilePath(skip int) string { _, path, _, _ := runtime.Caller(skip) return path } + +// for our cheatsheet script and integration tests. Not to be confused with finding the +// root directory of _any_ random repo. +func GetLazygitRootDirectory() string { + path, err := os.Getwd() + if err != nil { + panic(err) + } + + for { + _, err := os.Stat(filepath.Join(path, ".git")) + + if err == nil { + return path + } + + if !os.IsNotExist(err) { + panic(err) + } + + path = filepath.Dir(path) + + if path == "/" { + log.Fatal("must run in lazygit folder or child folder") + } + } +} |