Add uninstall command + tests for uninstall

This commit is contained in:
David Dworken 2022-11-02 19:41:49 -07:00
parent 422556f812
commit 17ae676da0
No known key found for this signature in database
9 changed files with 148 additions and 27 deletions

View File

@ -156,6 +156,7 @@ func TestParameterized(t *testing.T) {
t.Run("testControlR/"+tester.ShellName(), func(t *testing.T) { testControlR(t, tester, tester.ShellName()) })
t.Run("testHandleUpgradedFeatures/"+tester.ShellName(), func(t *testing.T) { testHandleUpgradedFeatures(t, tester) })
t.Run("testCustomColumns/"+tester.ShellName(), func(t *testing.T) { testCustomColumns(t, tester) })
t.Run("testUninstall/"+tester.ShellName(), func(t *testing.T) { testUninstall(t, tester) })
}
t.Run("testControlR/fish", func(t *testing.T) { testControlR(t, bashTester{}, "fish") })
}
@ -2003,6 +2004,40 @@ echo bar`)
compareGoldens(t, out, fmt.Sprintf("testCustomColumns-tquery-%s-isAction=%v", tester.ShellName(), (os.Getenv("GITHUB_ACTION") != "")))
}
func testUninstall(t *testing.T, tester shellTester) {
// Setup
defer testutils.BackupAndRestore(t)()
installHishtory(t, tester, "")
// Record a few commands and check that they get recorded
tester.RunInteractiveShell(t, `echo foo
echo baz`)
out := tester.RunInteractiveShell(t, `hishtory export -pipefail`)
compareGoldens(t, out, "testUninstall-recorded")
// And then uninstall
out, err := tester.RunInteractiveShellRelaxed(t, `yes | hishtory uninstall`)
testutils.Check(t, err)
compareGoldens(t, out, "testUninstall-uninstall")
// And check that hishtory has been uninstalled
out, err = tester.RunInteractiveShellRelaxed(t, `echo foo
hishtory
echo bar`)
testutils.Check(t, err)
compareGoldens(t, out, "testUninstall-post-uninstall")
// And check again, but in a way that shows the full terminal output
if os.Getenv("GITHUB_ACTION") == "" {
out = captureTerminalOutput(t, tester, []string{
"echo SPACE foo ENTER",
"hishtory ENTER",
"echo SPACE bar ENTER",
})
compareGoldens(t, out, "testUninstall-post-uninstall-"+tester.ShellName())
}
}
type deviceSet struct {
deviceMap *map[device]deviceOp
currentDevice *device

View File

@ -0,0 +1,2 @@
foo
bar

View File

@ -0,0 +1,8 @@
bash-5.2$ source /Users/david/.bashrc
bash-5.2$ echo foo
foo
bash-5.2$ hishtory
bash: hishtory: command not found
bash-5.2$ echo bar
bar
bash-5.2$

View File

@ -0,0 +1,7 @@
david@Davids-MacBook-Air hishtory % echo foo
foo
david@Davids-MacBook-Air hishtory % hishtory
zsh: command not found: hishtory
david@Davids-MacBook-Air hishtory % echo bar
bar
david@Davids-MacBook-Air hishtory %

View File

@ -0,0 +1,2 @@
echo foo
echo baz

View File

@ -0,0 +1 @@
Are you sure you want to uninstall hiSHtory and delete all locally saved history data [y/N]Successfully uninstalled hishtory, please restart your terminal...

View File

@ -672,6 +672,10 @@ func handleUpgradedFeatures() error {
return hctx.SetConfig(config)
}
func getFishConfigPath(homedir string) string {
return path.Join(homedir, data.HISHTORY_PATH, "config.fish")
}
func configureFish(homedir, binaryPath string) error {
// Check if fish is installed
_, err := exec.LookPath("fish")
@ -679,7 +683,6 @@ func configureFish(homedir, binaryPath string) error {
return nil
}
// Create the file we're going to source. Do this no matter what in case there are updates to it.
fishConfigPath := path.Join(homedir, data.HISHTORY_PATH, "config.fish")
configContents := ConfigFishContents
if os.Getenv("HISHTORY_TEST") != "" {
testConfig, err := tweakConfigForTests(ConfigFishContents)
@ -688,7 +691,7 @@ func configureFish(homedir, binaryPath string) error {
}
configContents = testConfig
}
err = ioutil.WriteFile(fishConfigPath, []byte(configContents), 0o644)
err = ioutil.WriteFile(getFishConfigPath(homedir), []byte(configContents), 0o644)
if err != nil {
return fmt.Errorf("failed to write config.zsh file: %v", err)
}
@ -710,28 +713,35 @@ func configureFish(homedir, binaryPath string) error {
return fmt.Errorf("failed to append to ~/.config/fish/config.fish: %v", err)
}
defer f.Close()
_, err = f.WriteString("\n# Hishtory Config:\nexport PATH=\"$PATH:" + path.Join(homedir, data.HISHTORY_PATH) + "\"\nsource " + fishConfigPath + "\n")
_, err = f.WriteString(getFishConfigFragment(homedir))
if err != nil {
return fmt.Errorf("failed to append to zshrc: %v", err)
}
return nil
}
func getFishConfigFragment(homedir string) string {
return "\n# Hishtory Config:\nexport PATH=\"$PATH:" + path.Join(homedir, data.HISHTORY_PATH) + "\"\nsource " + getFishConfigPath(homedir) + "\n"
}
func isFishConfigured(homedir string) (bool, error) {
_, err := os.Stat(path.Join(homedir, ".config/fish/config.fish"))
if errors.Is(err, os.ErrNotExist) {
return false, nil
}
bashrc, err := ioutil.ReadFile(path.Join(homedir, ".config/fish/config.fish"))
fishConfig, err := ioutil.ReadFile(path.Join(homedir, ".config/fish/config.fish"))
if err != nil {
return false, fmt.Errorf("failed to read ~/.config/fish/config.fish: %v", err)
}
return strings.Contains(string(bashrc), "# Hishtory Config:"), nil
return strings.Contains(string(fishConfig), "# Hishtory Config:"), nil
}
func getZshConfigPath(homedir string) string {
return path.Join(homedir, data.HISHTORY_PATH, "config.zsh")
}
func configureZshrc(homedir, binaryPath string) error {
// Create the file we're going to source in our zshrc. Do this no matter what in case there are updates to it.
zshConfigPath := path.Join(homedir, data.HISHTORY_PATH, "config.zsh")
configContents := ConfigZshContents
if os.Getenv("HISHTORY_TEST") != "" {
testConfig, err := tweakConfigForTests(configContents)
@ -740,7 +750,7 @@ func configureZshrc(homedir, binaryPath string) error {
}
configContents = testConfig
}
err := ioutil.WriteFile(zshConfigPath, []byte(configContents), 0o644)
err := ioutil.WriteFile(getZshConfigPath(homedir), []byte(configContents), 0o644)
if err != nil {
return fmt.Errorf("failed to write config.zsh file: %v", err)
}
@ -758,13 +768,17 @@ func configureZshrc(homedir, binaryPath string) error {
return fmt.Errorf("failed to append to zshrc: %v", err)
}
defer f.Close()
_, err = f.WriteString("\n# Hishtory Config:\nexport PATH=\"$PATH:" + path.Join(homedir, data.HISHTORY_PATH) + "\"\nsource " + zshConfigPath + "\n")
_, err = f.WriteString(getZshConfigFragment(homedir))
if err != nil {
return fmt.Errorf("failed to append to zshrc: %v", err)
}
return nil
}
func getZshConfigFragment(homedir string) string {
return "\n# Hishtory Config:\nexport PATH=\"$PATH:" + path.Join(homedir, data.HISHTORY_PATH) + "\"\nsource " + getZshConfigPath(homedir) + "\n"
}
func isZshConfigured(homedir string) (bool, error) {
_, err := os.Stat(path.Join(homedir, ".zshrc"))
if errors.Is(err, os.ErrNotExist) {
@ -777,9 +791,12 @@ func isZshConfigured(homedir string) (bool, error) {
return strings.Contains(string(bashrc), "# Hishtory Config:"), nil
}
func getBashConfigPath(homedir string) string {
return path.Join(homedir, data.HISHTORY_PATH, "config.sh")
}
func configureBashrc(homedir, binaryPath string) error {
// Create the file we're going to source in our bashrc. Do this no matter what in case there are updates to it.
bashConfigPath := path.Join(homedir, data.HISHTORY_PATH, "config.sh")
configContents := ConfigShContents
if os.Getenv("HISHTORY_TEST") != "" {
testConfig, err := tweakConfigForTests(ConfigShContents)
@ -788,7 +805,7 @@ func configureBashrc(homedir, binaryPath string) error {
}
configContents = testConfig
}
err := ioutil.WriteFile(bashConfigPath, []byte(configContents), 0o644)
err := ioutil.WriteFile(getBashConfigPath(homedir), []byte(configContents), 0o644)
if err != nil {
return fmt.Errorf("failed to write config.sh file: %v", err)
}
@ -806,13 +823,17 @@ func configureBashrc(homedir, binaryPath string) error {
return fmt.Errorf("failed to append to bashrc: %v", err)
}
defer f.Close()
_, err = f.WriteString("\n# Hishtory Config:\nexport PATH=\"$PATH:" + path.Join(homedir, data.HISHTORY_PATH) + "\"\nsource " + bashConfigPath + "\n")
_, err = f.WriteString(getBashConfigFragment(homedir))
if err != nil {
return fmt.Errorf("failed to append to bashrc: %v", err)
}
return nil
}
func getBashConfigFragment(homedir string) string {
return "\n# Hishtory Config:\nexport PATH=\"$PATH:" + path.Join(homedir, data.HISHTORY_PATH) + "\"\nsource " + getBashConfigPath(homedir) + "\n"
}
func isBashConfigured(homedir string) (bool, error) {
_, err := os.Stat(path.Join(homedir, ".bashrc"))
if errors.Is(err, os.ErrNotExist) {
@ -1551,3 +1572,46 @@ func tokenize(query string) ([]string, error) {
}
return strings.Split(query, " "), nil
}
func stripLines(filePath, lines string) error {
origContents, err := os.ReadFile(filePath)
if err != nil {
return err
}
linesToBeRemoved := make(map[string]bool, 0)
for _, line := range strings.Split(lines, "\n") {
if strings.TrimSpace(line) != "" {
linesToBeRemoved[line] = true
}
}
ret := ""
for _, line := range strings.Split(string(origContents), "\n") {
if !linesToBeRemoved[line] {
ret += line
ret += "\n"
}
}
return os.WriteFile(filePath, []byte(ret), 0644)
}
func Uninstall(ctx *context.Context) error {
homedir := hctx.GetHome(ctx)
err := stripLines(path.Join(homedir, ".bashrc"), getBashConfigFragment(homedir))
if err != nil {
return err
}
err = stripLines(path.Join(homedir, ".zshrc"), getZshConfigFragment(homedir))
if err != nil {
return err
}
err = stripLines(path.Join(homedir, ".config/fish/config.fish"), getFishConfigFragment(homedir))
if err != nil {
return err
}
err = os.RemoveAll(path.Join(homedir, ".hishtory"))
if err != nil {
return err
}
fmt.Println("Successfully uninstalled hishtory, please restart your terminal...")
return nil
}

View File

@ -87,6 +87,16 @@ func main() {
}
}
}
case "uninstall":
fmt.Printf("Are you sure you want to uninstall hiSHtory and delete all locally saved history data [y/N]")
reader := bufio.NewReader(os.Stdin)
resp, err := reader.ReadString('\n')
lib.CheckFatalError(err)
if strings.TrimSpace(resp) != "y" {
fmt.Printf("Aborting uninstall per user response of %#v\n", strings.TrimSpace(resp))
return
}
lib.CheckFatalError(lib.Uninstall(hctx.MakeContext()))
case "import":
ctx := hctx.MakeContext()
numImported, err := lib.ImportHistory(ctx, true)

View File

@ -4,7 +4,6 @@ import (
"bytes"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"os"
@ -43,17 +42,8 @@ func BackupAndRestore(t *testing.T) func() {
return BackupAndRestoreWithId(t, "")
}
func DeleteBakFiles(t *testing.T) {
homedir, err := os.UserHomeDir()
checkError(err)
entries, err := ioutil.ReadDir(path.Join(homedir, data.HISHTORY_PATH))
checkError(err)
for _, entry := range entries {
fmt.Println(entry.Name())
if strings.HasSuffix(entry.Name(), ".bak") {
checkError(os.Remove(path.Join(homedir, data.HISHTORY_PATH, entry.Name())))
}
}
func getBackPath(file, id string) string {
return strings.Replace(file, data.HISHTORY_PATH, data.HISHTORY_PATH+".test", 1) + id
}
func BackupAndRestoreWithId(t *testing.T, id string) func() {
@ -62,6 +52,7 @@ func BackupAndRestoreWithId(t *testing.T, id string) func() {
Check(t, err)
initialWd, err := os.Getwd()
Check(t, err)
Check(t, os.MkdirAll(path.Join(homedir, data.HISHTORY_PATH+".test"), os.ModePerm))
renameFiles := []string{
path.Join(homedir, data.HISHTORY_PATH, data.DB_PATH),
@ -77,7 +68,7 @@ func BackupAndRestoreWithId(t *testing.T, id string) func() {
}
for _, file := range renameFiles {
touchFile(file)
_ = os.Rename(file, file+id+".bak")
_ = os.Rename(file, getBackPath(file, id))
}
copyFiles := []string{
path.Join(homedir, ".zshrc"),
@ -85,17 +76,18 @@ func BackupAndRestoreWithId(t *testing.T, id string) func() {
}
for _, file := range copyFiles {
touchFile(file)
_ = copy(file, file+id+".bak")
_ = copy(file, getBackPath(file, id))
}
configureZshrc(homedir)
touchFile(path.Join(homedir, ".bash_history"))
touchFile(path.Join(homedir, ".zsh_history"))
return func() {
Check(t, os.MkdirAll(path.Join(homedir, data.HISHTORY_PATH), os.ModePerm))
for _, file := range renameFiles {
checkError(os.Rename(file+id+".bak", file))
checkError(os.Rename(getBackPath(file, id), file))
}
for _, file := range copyFiles {
checkError(copy(file+id+".bak", file))
checkError(copy(getBackPath(file, id), file))
}
cmd := exec.Command("killall", "hishtory")
stdout, err := cmd.Output()