Finish up and use device ID and name. Changes.

- Setup and use Device ID and name - get rid of hostname use.
- Use datadir
- Add uuid go binary, remove script
- Install-utils: migrations of config and history, device setup
- Use nohup on Linux (consistency with Darwin)
- Installl script
pull/184/head
Simon Let 3 years ago
parent b68519f50e
commit 1a584c78f6
  1. 12
      .goreleaser.yml
  2. 6
      Makefile
  3. 21
      cmd/cli/main.go
  4. 13
      cmd/collect/main.go
  5. 4
      cmd/config/main.go
  6. 6
      cmd/control/cmd/root.go
  7. 6
      cmd/control/main.go
  8. 73
      cmd/daemon/main.go
  9. 5
      cmd/daemon/record.go
  10. 10
      cmd/daemon/run-server.go
  11. 26
      cmd/generate-uuid/main.go
  12. 27
      cmd/install-utils/device.go
  13. 42
      cmd/install-utils/main.go
  14. 111
      cmd/install-utils/migrate.go
  15. 4
      cmd/postcollect/main.go
  16. 4
      cmd/session-init/main.go
  17. 2
      go.mod
  18. 2
      go.sum
  19. 138
      internal/device/device.go
  20. 54
      internal/futil/futil.go
  21. 4
      internal/logger/logger.go
  22. 19
      internal/output/output.go
  23. 28
      internal/recio/read.go
  24. 1
      internal/recio/write.go
  25. 2
      internal/recordint/indexed.go
  26. 4
      scripts/hooks.sh
  27. 62
      scripts/install.sh
  28. 2
      scripts/reshctl.sh
  29. 4
      scripts/shellrc.sh
  30. 3
      scripts/util.sh
  31. 35
      scripts/uuid.sh
  32. 2
      scripts/widgets.sh

@ -94,18 +94,18 @@ builds:
- arm - arm
- arm64 - arm64
- -
id: "config-setup" id: "install-utils"
main: ./cmd/config-setup main: ./cmd/install-utils
binary: bin/resh-config-setup binary: bin/resh-install-utils
goarch: goarch:
- 386 - 386
- amd64 - amd64
- arm - arm
- arm64 - arm64
- -
id: "install-utils" id: "generate-uuid"
main: ./cmd/install-utils main: ./cmd/generate-uuid
binary: bin/resh-install-utils binary: bin/resh-generate-uuid
goarch: goarch:
- 386 - 386
- amd64 - amd64

@ -7,7 +7,7 @@ GOFLAGS=-ldflags "-X main.version=${VERSION} -X main.commit=${COMMIT} -X main.de
build: submodules bin/resh-session-init bin/resh-collect bin/resh-postcollect\ build: submodules bin/resh-session-init bin/resh-collect bin/resh-postcollect\
bin/resh-daemon bin/resh-control bin/resh-config bin/resh-cli\ bin/resh-daemon bin/resh-control bin/resh-config bin/resh-cli\
bin/resh-install-utils bin/resh-install-utils bin/resh-generate-uuid
install: build install: build
scripts/install.sh scripts/install.sh
@ -22,11 +22,11 @@ rebuild:
make build make build
clean: clean:
rm -f bin/* rm -f -- bin/*
uninstall: uninstall:
# Uninstalling ... # Uninstalling ...
-rm -rf ~/.resh/ -rm -rf -- ~/.resh/
go_files = $(shell find -name '*.go') go_files = $(shell find -name '*.go')
bin/resh-%: $(go_files) bin/resh-%: $(go_files)

@ -16,6 +16,8 @@ import (
"github.com/awesome-gocui/gocui" "github.com/awesome-gocui/gocui"
"github.com/curusarn/resh/internal/cfg" "github.com/curusarn/resh/internal/cfg"
"github.com/curusarn/resh/internal/datadir"
"github.com/curusarn/resh/internal/device"
"github.com/curusarn/resh/internal/logger" "github.com/curusarn/resh/internal/logger"
"github.com/curusarn/resh/internal/msg" "github.com/curusarn/resh/internal/msg"
"github.com/curusarn/resh/internal/output" "github.com/curusarn/resh/internal/output"
@ -29,14 +31,14 @@ import (
// info passed during build // info passed during build
var version string var version string
var commit string var commit string
var developement bool var development string
// special constant recognized by RESH wrappers // special constant recognized by RESH wrappers
const exitCodeExecute = 111 const exitCodeExecute = 111
func main() { func main() {
config, errCfg := cfg.New() config, errCfg := cfg.New()
logger, _ := logger.New("search-app", config.LogLevel, developement) logger, _ := logger.New("search-app", config.LogLevel, development)
defer logger.Sync() // flushes buffer, if any defer logger.Sync() // flushes buffer, if any
if errCfg != nil { if errCfg != nil {
logger.Error("Error while getting configuration", zap.Error(errCfg)) logger.Error("Error while getting configuration", zap.Error(errCfg))
@ -50,7 +52,6 @@ func main() {
func runReshCli(out *output.Output, config cfg.Config) (string, int) { func runReshCli(out *output.Output, config cfg.Config) (string, int) {
sessionID := flag.String("sessionID", "", "resh generated session id") sessionID := flag.String("sessionID", "", "resh generated session id")
host := flag.String("host", "", "host")
pwd := flag.String("pwd", "", "present working directory") pwd := flag.String("pwd", "", "present working directory")
gitOriginRemote := flag.String("gitOriginRemote", "DEFAULT", "git origin remote") gitOriginRemote := flag.String("gitOriginRemote", "DEFAULT", "git origin remote")
query := flag.String("query", "", "search query") query := flag.String("query", "", "search query")
@ -62,15 +63,20 @@ func runReshCli(out *output.Output, config cfg.Config) (string, int) {
if *sessionID == "" { if *sessionID == "" {
out.Fatal(errMsg, errors.New("missing option --sessionId")) out.Fatal(errMsg, errors.New("missing option --sessionId"))
} }
if *host == "" {
out.Fatal(errMsg, errors.New("missing option --host"))
}
if *pwd == "" { if *pwd == "" {
out.Fatal(errMsg, errors.New("missing option --pwd")) out.Fatal(errMsg, errors.New("missing option --pwd"))
} }
if *gitOriginRemote == "DEFAULT" { if *gitOriginRemote == "DEFAULT" {
out.Fatal(errMsg, errors.New("missing option --gitOriginRemote")) out.Fatal(errMsg, errors.New("missing option --gitOriginRemote"))
} }
dataDir, err := datadir.GetPath()
if err != nil {
out.Fatal("Could not get user data directory", err)
}
deviceName, err := device.GetName(dataDir)
if err != nil {
out.Fatal("Could not get device name", err)
}
g, err := gocui.NewGui(gocui.OutputNormal, false) g, err := gocui.NewGui(gocui.OutputNormal, false)
if err != nil { if err != nil {
@ -100,11 +106,12 @@ func runReshCli(out *output.Output, config cfg.Config) (string, int) {
initialQuery: *query, initialQuery: *query,
} }
// TODO: Use device ID
layout := manager{ layout := manager{
out: out, out: out,
config: config, config: config,
sessionID: *sessionID, sessionID: *sessionID,
host: *host, host: deviceName,
pwd: *pwd, pwd: *pwd,
gitOriginRemote: *gitOriginRemote, gitOriginRemote: *gitOriginRemote,
s: &st, s: &st,

@ -22,11 +22,11 @@ import (
// info passed during build // info passed during build
var version string var version string
var commit string var commit string
var developement bool var development string
func main() { func main() {
config, errCfg := cfg.New() config, errCfg := cfg.New()
logger, _ := logger.New("collect", config.LogLevel, developement) logger, _ := logger.New("collect", config.LogLevel, development)
defer logger.Sync() // flushes buffer, if any defer logger.Sync() // flushes buffer, if any
if errCfg != nil { if errCfg != nil {
logger.Error("Error while getting configuration", zap.Error(errCfg)) logger.Error("Error while getting configuration", zap.Error(errCfg))
@ -46,17 +46,12 @@ func main() {
home := flag.String("home", "", "$HOME") home := flag.String("home", "", "$HOME")
pwd := flag.String("pwd", "", "$PWD - present working directory") pwd := flag.String("pwd", "", "$PWD - present working directory")
// FIXME: get device ID
deviceID := flag.String("deviceID", "", "RESH device ID")
sessionID := flag.String("sessionID", "", "resh generated session ID") sessionID := flag.String("sessionID", "", "resh generated session ID")
recordID := flag.String("recordID", "", "resh generated record ID") recordID := flag.String("recordID", "", "resh generated record ID")
sessionPID := flag.Int("sessionPID", -1, "PID at the start of the terminal session") sessionPID := flag.Int("sessionPID", -1, "PID at the start of the terminal session")
shell := flag.String("shell", "", "current shell") shell := flag.String("shell", "", "current shell")
// logname := flag.String("logname", "", "$LOGNAME")
device := flag.String("device", "", "device name, usually $HOSTNAME")
// non-posix // non-posix
shlvl := flag.Int("shlvl", -1, "$SHLVL") shlvl := flag.Int("shlvl", -1, "$SHLVL")
@ -100,7 +95,6 @@ func main() {
Shell: *shell, Shell: *shell,
Rec: record.V1{ Rec: record.V1{
DeviceID: *deviceID,
SessionID: *sessionID, SessionID: *sessionID,
RecordID: *recordID, RecordID: *recordID,
@ -111,9 +105,6 @@ func main() {
Pwd: *pwd, Pwd: *pwd,
RealPwd: realPwd, RealPwd: realPwd,
// Logname: *logname,
Device: *device,
GitOriginRemote: *gitRemote, GitOriginRemote: *gitRemote,
Time: fmt.Sprintf("%.4f", time), Time: fmt.Sprintf("%.4f", time),

@ -14,11 +14,11 @@ import (
// info passed during build // info passed during build
var version string var version string
var commit string var commit string
var developement bool var development string
func main() { func main() {
config, errCfg := cfg.New() config, errCfg := cfg.New()
logger, _ := logger.New("config", config.LogLevel, developement) logger, _ := logger.New("config", config.LogLevel, development)
defer logger.Sync() // flushes buffer, if any defer logger.Sync() // flushes buffer, if any
if errCfg != nil { if errCfg != nil {
logger.Error("Error while getting configuration", zap.Error(errCfg)) logger.Error("Error while getting configuration", zap.Error(errCfg))

@ -7,10 +7,8 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
// info passed during build
var version string var version string
var commit string var commit string
var developement bool
// globals // globals
var config cfg.Config var config cfg.Config
@ -22,12 +20,12 @@ var rootCmd = &cobra.Command{
} }
// Execute reshctl // Execute reshctl
func Execute(ver, com string) { func Execute(ver, com, development string) {
version = ver version = ver
commit = com commit = com
config, errCfg := cfg.New() config, errCfg := cfg.New()
logger, _ := logger.New("reshctl", config.LogLevel, developement) logger, _ := logger.New("reshctl", config.LogLevel, development)
defer logger.Sync() // flushes buffer, if any defer logger.Sync() // flushes buffer, if any
out = output.New(logger, "ERROR") out = output.New(logger, "ERROR")
if errCfg != nil { if errCfg != nil {

@ -4,12 +4,10 @@ import (
"github.com/curusarn/resh/cmd/control/cmd" "github.com/curusarn/resh/cmd/control/cmd"
) )
// version from git set during build
var version string var version string
// commit from git set during build
var commit string var commit string
var development string
func main() { func main() {
cmd.Execute(version, commit) cmd.Execute(version, commit, development)
} }

@ -10,6 +10,8 @@ import (
"strings" "strings"
"github.com/curusarn/resh/internal/cfg" "github.com/curusarn/resh/internal/cfg"
"github.com/curusarn/resh/internal/datadir"
"github.com/curusarn/resh/internal/device"
"github.com/curusarn/resh/internal/httpclient" "github.com/curusarn/resh/internal/httpclient"
"github.com/curusarn/resh/internal/logger" "github.com/curusarn/resh/internal/logger"
"go.uber.org/zap" "go.uber.org/zap"
@ -18,11 +20,11 @@ import (
// info passed during build // info passed during build
var version string var version string
var commit string var commit string
var developement bool var development string
func main() { func main() {
config, errCfg := cfg.New() config, errCfg := cfg.New()
logger, err := logger.New("daemon", config.LogLevel, developement) logger, err := logger.New("daemon", config.LogLevel, development)
if err != nil { if err != nil {
fmt.Printf("Error while creating logger: %v", err) fmt.Printf("Error while creating logger: %v", err)
} }
@ -32,48 +34,60 @@ func main() {
} }
sugar := logger.Sugar() sugar := logger.Sugar()
d := daemon{sugar: sugar} d := daemon{sugar: sugar}
sugar.Infow("Deamon starting ...", sugar.Infow("Daemon starting ...",
"version", version, "version", version,
"commit", commit, "commit", commit,
) )
dataDir, err := datadir.MakePath()
// TODO: rethink PID file and logs location if err != nil {
sugar.Fatalw("Could not get user data directory", zap.Error(err))
}
homeDir, err := os.UserHomeDir() homeDir, err := os.UserHomeDir()
if err != nil { if err != nil {
sugar.Fatalw("Could not get user home dir", zap.Error(err)) sugar.Fatalw("Could not get user home directory", zap.Error(err))
} }
PIDFile := filepath.Join(homeDir, ".resh/resh.pid") // TODO: These paths should be probably defined in a package
reshHistoryPath := filepath.Join(homeDir, ".resh_history.json") pidFile := filepath.Join(dataDir, "daemon.pid")
reshHistoryPath := filepath.Join(dataDir, "history.reshjson")
bashHistoryPath := filepath.Join(homeDir, ".bash_history") bashHistoryPath := filepath.Join(homeDir, ".bash_history")
zshHistoryPath := filepath.Join(homeDir, ".zsh_history") zshHistoryPath := filepath.Join(homeDir, ".zsh_history")
deviceID, err := device.GetID(dataDir)
if err != nil {
sugar.Fatalw("Could not get resh device ID", zap.Error(err))
}
deviceName, err := device.GetName(dataDir)
if err != nil {
sugar.Fatalw("Could not get resh device name", zap.Error(err))
}
sugar = sugar.With(zap.Int("daemonPID", os.Getpid())) sugar = sugar.With(zap.Int("daemonPID", os.Getpid()))
res, err := d.isDaemonRunning(config.Port) res, err := d.isDaemonRunning(config.Port)
if err != nil { if err != nil {
sugar.Errorw("Error while checking daemon status - "+ sugar.Errorw("Error while checking daemon status - it's probably not running",
"it's probably not running", "error", err) "error", err)
} }
if res { if res {
sugar.Errorw("Daemon is already running - exiting!") sugar.Errorw("Daemon is already running - exiting!")
return return
} }
_, err = os.Stat(PIDFile) _, err = os.Stat(pidFile)
if err == nil { if err == nil {
sugar.Warn("Pidfile exists") sugar.Warnw("PID file exists",
"PIDFile", pidFile)
// kill daemon // kill daemon
err = d.killDaemon(PIDFile) err = d.killDaemon(pidFile)
if err != nil { if err != nil {
sugar.Errorw("Could not kill daemon", sugar.Errorw("Could not kill daemon",
"error", err, "error", err,
) )
} }
} }
err = ioutil.WriteFile(PIDFile, []byte(strconv.Itoa(os.Getpid())), 0644) err = ioutil.WriteFile(pidFile, []byte(strconv.Itoa(os.Getpid())), 0644)
if err != nil { if err != nil {
sugar.Fatalw("Could not create pidfile", sugar.Fatalw("Could not create PID file",
"error", err, "error", err,
"PIDFile", PIDFile, "PIDFile", pidFile,
) )
} }
server := Server{ server := Server{
@ -82,12 +96,15 @@ func main() {
reshHistoryPath: reshHistoryPath, reshHistoryPath: reshHistoryPath,
bashHistoryPath: bashHistoryPath, bashHistoryPath: bashHistoryPath,
zshHistoryPath: zshHistoryPath, zshHistoryPath: zshHistoryPath,
deviceID: deviceID,
deviceName: deviceName,
} }
server.Run() server.Run()
sugar.Infow("Removing PID file ...", sugar.Infow("Removing PID file ...",
"PIDFile", PIDFile, "PIDFile", pidFile,
) )
err = os.Remove(PIDFile) err = os.Remove(pidFile)
if err != nil { if err != nil {
sugar.Errorw("Could not delete PID file", "error", err) sugar.Errorw("Could not delete PID file", "error", err)
} }
@ -98,16 +115,6 @@ type daemon struct {
sugar *zap.SugaredLogger sugar *zap.SugaredLogger
} }
func (d *daemon) getEnvOrPanic(envVar string) string {
val, found := os.LookupEnv(envVar)
if !found {
d.sugar.Fatalw("Required env variable is not set",
"variableName", envVar,
)
}
return val
}
func (d *daemon) isDaemonRunning(port int) (bool, error) { func (d *daemon) isDaemonRunning(port int) (bool, error) {
url := "http://localhost:" + strconv.Itoa(port) + "/status" url := "http://localhost:" + strconv.Itoa(port) + "/status"
client := httpclient.New() client := httpclient.New()
@ -119,14 +126,14 @@ func (d *daemon) isDaemonRunning(port int) (bool, error) {
return true, nil return true, nil
} }
func (d *daemon) killDaemon(pidfile string) error { func (d *daemon) killDaemon(pidFile string) error {
dat, err := ioutil.ReadFile(pidfile) dat, err := ioutil.ReadFile(pidFile)
if err != nil { if err != nil {
d.sugar.Errorw("Reading pid file failed", d.sugar.Errorw("Reading PID file failed",
"PIDFile", pidfile, "PIDFile", pidFile,
"error", err) "error", err)
} }
d.sugar.Infow("Succesfully read PID file", "contents", string(dat)) d.sugar.Infow("Successfully read PID file", "contents", string(dat))
pid, err := strconv.Atoi(strings.TrimSuffix(string(dat), "\n")) pid, err := strconv.Atoi(strings.TrimSuffix(string(dat), "\n"))
if err != nil { if err != nil {
return fmt.Errorf("could not parse PID file contents: %w", err) return fmt.Errorf("could not parse PID file contents: %w", err)

@ -19,6 +19,9 @@ func NewRecordHandler(sugar *zap.SugaredLogger, subscribers []chan recordint.Col
type recordHandler struct { type recordHandler struct {
sugar *zap.SugaredLogger sugar *zap.SugaredLogger
subscribers []chan recordint.Collect subscribers []chan recordint.Collect
deviceID string
deviceName string
} }
func (h *recordHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (h *recordHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
@ -51,6 +54,8 @@ func (h *recordHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
"cmdLine", rec.Rec.CmdLine, "cmdLine", rec.Rec.CmdLine,
"part", part, "part", part,
) )
rec.Rec.DeviceID = h.deviceID
rec.Rec.Device = h.deviceName
sugar.Debugw("Got record, sending to subscribers ...") sugar.Debugw("Got record, sending to subscribers ...")
for _, sub := range h.subscribers { for _, sub := range h.subscribers {
sub <- rec sub <- rec

@ -23,6 +23,9 @@ type Server struct {
reshHistoryPath string reshHistoryPath string
bashHistoryPath string bashHistoryPath string
zshHistoryPath string zshHistoryPath string
deviceID string
deviceName string
} }
func (s *Server) Run() { func (s *Server) Run() {
@ -63,7 +66,12 @@ func (s *Server) Run() {
// handlers // handlers
mux := http.NewServeMux() mux := http.NewServeMux()
mux.Handle("/status", &statusHandler{sugar: s.sugar}) mux.Handle("/status", &statusHandler{sugar: s.sugar})
mux.Handle("/record", &recordHandler{sugar: s.sugar, subscribers: recordSubscribers}) mux.Handle("/record", &recordHandler{
sugar: s.sugar,
subscribers: recordSubscribers,
deviceID: s.deviceID,
deviceName: s.deviceName,
})
mux.Handle("/session_init", &sessionInitHandler{sugar: s.sugar, subscribers: sessionInitSubscribers}) mux.Handle("/session_init", &sessionInitHandler{sugar: s.sugar, subscribers: sessionInitSubscribers})
mux.Handle("/dump", &dumpHandler{sugar: s.sugar, histfileBox: histfileBox}) mux.Handle("/dump", &dumpHandler{sugar: s.sugar, histfileBox: histfileBox})

@ -0,0 +1,26 @@
package main
import (
"fmt"
"os"
"github.com/google/uuid"
)
// Small utility to generate UUID's using google/uuid golang package
// Doesn't check arguments
// Exits with status 1 on error
func main() {
rnd, err := uuid.NewRandom()
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: could not get new random source: %v", err)
os.Exit(1)
}
id := rnd.String()
if id == "" {
fmt.Fprintf(os.Stderr, "ERROR: got invalid UUID from package")
os.Exit(1)
}
// No newline
fmt.Print(id)
}

@ -0,0 +1,27 @@
package main
import (
"fmt"
"os"
"github.com/curusarn/resh/internal/datadir"
"github.com/curusarn/resh/internal/device"
)
func setupDevice() {
dataDir, err := datadir.MakePath()
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: Failed to get/setup data directory: %v\n", err)
os.Exit(1)
}
err = device.SetupName(dataDir)
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: Failed to check/setup device name: %v\n", err)
os.Exit(1)
}
err = device.SetupID(dataDir)
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: Failed to check/setup device ID: %v\n", err)
os.Exit(1)
}
}

@ -3,17 +3,39 @@ package main
import ( import (
"fmt" "fmt"
"os" "os"
"github.com/curusarn/resh/internal/cfg"
"github.com/curusarn/resh/internal/logger"
"github.com/curusarn/resh/internal/output"
"go.uber.org/zap"
) )
// info passed during build // info passed during build
var version string var version string
var commit string var commit string
var developement bool var development string
func main() { func main() {
config, errCfg := cfg.New()
logger, err := logger.New("install-utils", config.LogLevel, development)
if err != nil {
fmt.Printf("Error while creating logger: %v", err)
}
defer logger.Sync() // flushes buffer, if any
if errCfg != nil {
logger.Error("Error while getting configuration", zap.Error(errCfg))
}
sugar := logger.Sugar()
sugar.Infow("Install-utils invoked ...",
"version", version,
"commit", commit,
)
out := output.New(logger, "install-utils")
if len(os.Args) < 2 { if len(os.Args) < 2 {
fmt.Fprintf(os.Stderr, "ERROR: Not eonugh arguments\n") out.ErrorWOErr("ERROR: Not enough arguments\n")
printUsage(os.Stderr) printUsage(os.Stderr)
os.Exit(1)
} }
command := os.Args[1] command := os.Args[1]
switch command { switch command {
@ -24,27 +46,31 @@ func main() {
case "migrate-config": case "migrate-config":
migrateConfig() migrateConfig()
case "migrate-history": case "migrate-history":
migrateHistory() migrateHistory(out)
case "setup-device":
setupDevice()
case "help": case "help":
printUsage(os.Stdout) printUsage(os.Stdout)
default: default:
fmt.Fprintf(os.Stderr, "ERROR: Unknown command: %s\n", command) out.ErrorWOErr(fmt.Sprintf("ERROR: Unknown command: %s\n", command))
printUsage(os.Stderr) printUsage(os.Stderr)
os.Exit(1)
} }
} }
func printUsage(f *os.File) { func printUsage(f *os.File) {
usage := ` usage := `
USAGE: ./install-utils COMMAND USAGE: ./install-utils COMMAND
Utils used during RESH instalation. Utils used during RESH installation.
COMMANDS: COMMANDS:
backup backup resh installation and data backup backup resh installation and data
rollback restore resh installation and data from backup rollback restore resh installation and data from backup
migrate-config update config to reflect updates migrate-config update config to latest format
migrate-history update history to reflect updates migrate-history update history to latest format
setup-device setup device name and device ID
help show this help help show this help
` `
fmt.Fprintf(f, usage) fmt.Fprint(f, usage)
} }

@ -3,8 +3,14 @@ package main
import ( import (
"fmt" "fmt"
"os" "os"
"path"
"github.com/curusarn/resh/internal/cfg" "github.com/curusarn/resh/internal/cfg"
"github.com/curusarn/resh/internal/datadir"
"github.com/curusarn/resh/internal/futil"
"github.com/curusarn/resh/internal/output"
"github.com/curusarn/resh/internal/recio"
"github.com/curusarn/resh/record"
) )
func migrateConfig() { func migrateConfig() {
@ -23,18 +29,101 @@ func migrateConfig() {
} }
} }
func migrateHistory() { func migrateHistory(out *output.Output) {
// homeDir, err := os.UserHomeDir() migrateHistoryLocation(out)
// if err != nil { migrateHistoryFormat(out)
}
func migrateHistoryLocation(out *output.Output) {
dataDir, err := datadir.MakePath()
if err != nil {
out.Fatal("ERROR: Failed to get data directory", err)
}
// TODO: de-hardcode this
historyPath := path.Join(dataDir, "resh/history.reshjson")
exists, err := futil.FileExists(historyPath)
if err != nil {
out.Fatal("ERROR: Failed to check history file", err)
}
if exists {
out.Info(fmt.Sprintf("Found history file in '%s'", historyPath))
return
}
// } homeDir, err := os.UserHomeDir()
if err != nil {
out.Fatal("ERROR: Failed to get user home directory", err)
}
// TODO: Find history in: legacyHistoryPaths := []string{
// - .resh/history.json (copy) - message user to delete the file once they confirm the new setup works path.Join(homeDir, ".resh_history.json"),
// - .resh_history.json (copy) - message user to delete the file once they confirm the new setup works path.Join(homeDir, ".resh/history.json"),
// - xdg_data/resh/history.reshjson }
for _, path := range legacyHistoryPaths {
exists, err = futil.FileExists(path)
if err != nil {
out.Fatal("ERROR: Failed to check legacy history file", err)
}
if exists {
out.Info(fmt.Sprintf("Copying history file to new location: '%s' -> '%s' ...", path, historyPath))
err = futil.CopyFile(path, historyPath)
if err != nil {
out.Fatal("ERROR: Failed to copy history file", err)
}
out.Info("History file copied successfully")
return
}
}
}
// Read xdg_data/resh/history.reshjson func migrateHistoryFormat(out *output.Output) {
// Write xdg_data/resh/history.reshjson dataDir, err := datadir.MakePath()
// the old format can be found in the backup dir if err != nil {
out.Fatal("ERROR: Could not get user data directory", err)
}
// TODO: de-hardcode this
historyPath := path.Join(dataDir, "history.reshjson")
historyPathBak := historyPath + ".bak"
exists, err := futil.FileExists(historyPath)
if err != nil {
out.Fatal("ERROR: Failed to check existence of history file", err)
}
if !exists {
out.ErrorWOErr("There is no history file - this is normal if you are installing RESH for the first time on this device")
err = futil.CreateFile(historyPath)
if err != nil {
out.Fatal("ERROR: Failed to create history file", err)
}
os.Exit(0)
}
err = futil.CopyFile(historyPath, historyPathBak)
if err != nil {
out.Fatal("ERROR: Could not back up history file", err)
}
rio := recio.New(out.Logger.Sugar())
recs, err := rio.ReadAndFixFile(historyPath, 3)
if err != nil {
out.Fatal("ERROR: Could not load history file", err)
}
// TODO: get rid of this conversion
var recsV1 []record.V1
for _, rec := range recs {
recsV1 = append(recsV1, rec.Rec)
}
err = rio.OverwriteFile(historyPath, recsV1)
if err != nil {
out.Error("ERROR: Could not update format of history file", err)
err = futil.CopyFile(historyPathBak, historyPath)
if err != nil {
out.Fatal("ERROR: Could not restore history file from backup!", err)
// TODO: history restoration tutorial
}
out.Info("History file was restored to the original form")
}
} }

@ -21,11 +21,11 @@ import (
// info passed during build // info passed during build
var version string var version string
var commit string var commit string
var developement bool var development string
func main() { func main() {
config, errCfg := cfg.New() config, errCfg := cfg.New()
logger, _ := logger.New("postcollect", config.LogLevel, developement) logger, _ := logger.New("postcollect", config.LogLevel, development)
defer logger.Sync() // flushes buffer, if any defer logger.Sync() // flushes buffer, if any
if errCfg != nil { if errCfg != nil {
logger.Error("Error while getting configuration", zap.Error(errCfg)) logger.Error("Error while getting configuration", zap.Error(errCfg))

@ -18,11 +18,11 @@ import (
// info passed during build // info passed during build
var version string var version string
var commit string var commit string
var developement bool var development string
func main() { func main() {
config, errCfg := cfg.New() config, errCfg := cfg.New()
logger, _ := logger.New("collect", config.LogLevel, developement) logger, _ := logger.New("collect", config.LogLevel, development)
defer logger.Sync() // flushes buffer, if any defer logger.Sync() // flushes buffer, if any
if errCfg != nil { if errCfg != nil {
logger.Error("Error while getting configuration", zap.Error(errCfg)) logger.Error("Error while getting configuration", zap.Error(errCfg))

@ -5,6 +5,8 @@ go 1.18
require ( require (
github.com/BurntSushi/toml v0.4.1 github.com/BurntSushi/toml v0.4.1
github.com/awesome-gocui/gocui v1.0.0 github.com/awesome-gocui/gocui v1.0.0
github.com/google/uuid v1.1.2
github.com/mattn/go-isatty v0.0.3
github.com/mitchellh/go-ps v1.0.0 github.com/mitchellh/go-ps v1.0.0
github.com/spf13/cobra v1.2.1 github.com/spf13/cobra v1.2.1
github.com/whilp/git-urls v1.0.0 github.com/whilp/git-urls v1.0.0

@ -143,6 +143,7 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
@ -187,6 +188,7 @@ github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=

@ -5,45 +5,113 @@ import (
"os" "os"
"path" "path"
"strings" "strings"
"github.com/curusarn/resh/internal/futil"
"github.com/google/uuid"
isatty "github.com/mattn/go-isatty"
) )
const fnameID = "device-id"
const fnameName = "device-name"
const filePerm = 0644
// Getters
func GetID(dataDir string) (string, error) { func GetID(dataDir string) (string, error) {
fname := "device-id" return readValue(dataDir, fnameID)
dat, err := os.ReadFile(path.Join(dataDir, fname)) }
func GetName(dataDir string) (string, error) {
return readValue(dataDir, fnameName)
}
// Install helpers
func SetupID(dataDir string) error {
return generateIDIfUnset(dataDir)
}
func SetupName(dataDir string) error {
return promptAndWriteNameIfUnset(dataDir)
}
func readValue(dataDir, fname string) (string, error) {
fpath := path.Join(dataDir, fname)
dat, err := os.ReadFile(fpath)
if err != nil { if err != nil {
return "", fmt.Errorf("could not read file with device-id: %w", err) return "", fmt.Errorf("could not read file with %s: %w", fname, err)
} }
id := strings.TrimRight(string(dat), "\n") val := strings.TrimRight(string(dat), "\n")
return id, nil return val, nil
} }
func GetName(dataDir string) (string, error) { func generateIDIfUnset(dataDir string) error {
fname := "device-name" fpath := path.Join(dataDir, fnameID)
dat, err := os.ReadFile(path.Join(dataDir, fname)) exists, err := futil.FileExists(fpath)
if err != nil { if err != nil {
return "", fmt.Errorf("could not read file with device-name: %w", err) return err
} }
name := strings.TrimRight(string(dat), "\n") if exists {
return name, nil return nil
} }
// TODO: implement, possibly with a better name rnd, err := uuid.NewRandom()
// func CheckID(dataDir string) (string, error) { if err != nil {
// fname := "device-id" return fmt.Errorf("could not get new random source: %w", err)
// dat, err := os.ReadFile(path.Join(dataDir, fname)) }
// if err != nil { id := rnd.String()
// return "", fmt.Errorf("could not read file with device-id: %w", err) if id == "" {
// } return fmt.Errorf("got invalid UUID from package")
// id := strings.TrimRight(string(dat), "\n") }
// return id, nil err = os.WriteFile(fpath, []byte(id), filePerm)
// } if err != nil {
// return fmt.Errorf("could not write generated ID to file: %w", err)
// func CheckName(dataDir string) (string, error) { }
// fname := "device-id" return nil
// dat, err := os.ReadFile(path.Join(dataDir, fname)) }
// if err != nil {
// return "", fmt.Errorf("could not read file with device-id: %w", err) func promptAndWriteNameIfUnset(dataDir string) error {
// } fpath := path.Join(dataDir, fnameName)
// id := strings.TrimRight(string(dat), "\n") exists, err := futil.FileExists(fpath)
// return id, nil if err != nil {
// } return err
}
if exists {
return nil
}
name, err := promptForName(fpath)
if err != nil {
return fmt.Errorf("error while prompting for input: %w", err)
}
err = os.WriteFile(fpath, []byte(name), filePerm)
if err != nil {
return fmt.Errorf("could not write name to file: %w", err)
}
return nil
}
func promptForName(fpath string) (string, error) {
// This function should be only ran from install-utils with attached terminal
if !isatty.IsTerminal(os.Stdout.Fd()) {
return "", fmt.Errorf("output is not a terminal - write name of this device to '%s' to bypass this error", fpath)
}
host, err := os.Hostname()
if err != nil {
return "", fmt.Errorf("could not get hostname (prompt default): %w", err)
}
hostStub := strings.Split(host, ".")[0]
fmt.Printf("\nPlease choose a short name for this device (default: '%s'): ", hostStub)
var input string
n, err := fmt.Scanln(&input)
if n != 1 {
return "", fmt.Errorf("expected 1 value from prompt got %d", n)
}
if err != nil {
return "", fmt.Errorf("scanln error: %w", err)
}
fmt.Printf("Input was: %s\n", input)
fmt.Printf("You can change the device name at any time by editing '%s' file\n", fpath)
return input, nil
}

@ -0,0 +1,54 @@
package futil
import (
"fmt"
"io"
"os"
)
func CopyFile(source, dest string) error {
from, err := os.Open(source)
if err != nil {
return err
}
defer from.Close()
// This is equivalent to: os.OpenFile(dest, os.O_RDWR|os.O_CREATE, 0666)
to, err := os.Create(dest)
if err != nil {
return err
}
defer to.Close()
_, err = io.Copy(to, from)
if err != nil {
return err
}
return nil
}
func FileExists(fpath string) (bool, error) {
_, err := os.Stat(fpath)
if err == nil {
// File exists
return true, nil
}
if os.IsNotExist(err) {
// File doesn't exist
return false, nil
}
// Any other error
return false, fmt.Errorf("could not stat file: %w", err)
}
func CreateFile(fpath string) error {
ff, err := os.Create(fpath)
if err != nil {
return err
}
err = ff.Close()
if err != nil {
return err
}
return nil
}

@ -9,7 +9,7 @@ import (
"go.uber.org/zap/zapcore" "go.uber.org/zap/zapcore"
) )
func New(executable string, level zapcore.Level, developement bool) (*zap.Logger, error) { func New(executable string, level zapcore.Level, development string) (*zap.Logger, error) {
dataDir, err := datadir.GetPath() dataDir, err := datadir.GetPath()
if err != nil { if err != nil {
return nil, fmt.Errorf("error while getting resh data dir: %w", err) return nil, fmt.Errorf("error while getting resh data dir: %w", err)
@ -18,7 +18,7 @@ func New(executable string, level zapcore.Level, developement bool) (*zap.Logger
loggerConfig := zap.NewProductionConfig() loggerConfig := zap.NewProductionConfig()
loggerConfig.OutputPaths = []string{logPath} loggerConfig.OutputPaths = []string{logPath}
loggerConfig.Level.SetLevel(level) loggerConfig.Level.SetLevel(level)
loggerConfig.Development = developement // DPanic panics in developement loggerConfig.Development = development == "true" // DPanic panics in development
logger, err := loggerConfig.Build() logger, err := loggerConfig.Build()
if err != nil { if err != nil {
return logger, fmt.Errorf("error while creating logger: %w", err) return logger, fmt.Errorf("error while creating logger: %w", err)

@ -7,7 +7,7 @@ import (
"go.uber.org/zap" "go.uber.org/zap"
) )
// Output wrapper for writting to logger and stdout/stderr at the same time // Output wrapper for writing to logger and stdout/stderr at the same time
// useful for errors that should be presented to the user // useful for errors that should be presented to the user
type Output struct { type Output struct {
Logger *zap.Logger Logger *zap.Logger
@ -22,21 +22,26 @@ func New(logger *zap.Logger, prefix string) *Output {
} }
func (f *Output) Info(msg string) { func (f *Output) Info(msg string) {
fmt.Fprintf(os.Stdout, msg) fmt.Printf("%s\n", msg)
f.Logger.Info(msg) f.Logger.Info(msg)
} }
func (f *Output) Error(msg string, err error) { func (f *Output) Error(msg string, err error) {
fmt.Fprintf(os.Stderr, "%s: %s: %v", f.ErrPrefix, msg, err) fmt.Fprintf(os.Stderr, "%s: %s: %v\n", f.ErrPrefix, msg, err)
f.Logger.Error(msg, zap.Error(err)) f.Logger.Error(msg, zap.Error(err))
} }
func (f *Output) ErrorWOErr(msg string) {
fmt.Fprintf(os.Stderr, "%s: %s\n", f.ErrPrefix, msg)
f.Logger.Error(msg)
}
func (f *Output) Fatal(msg string, err error) { func (f *Output) Fatal(msg string, err error) {
fmt.Fprintf(os.Stderr, "%s: %s: %v", f.ErrPrefix, msg, err) fmt.Fprintf(os.Stderr, "%s: %s: %v\n", f.ErrPrefix, msg, err)
f.Logger.Fatal(msg, zap.Error(err)) f.Logger.Fatal(msg, zap.Error(err))
} }
var msgDeamonNotRunning = `Resh-daemon didn't respond - it's probably not running. var msgDaemonNotRunning = `Resh-daemon didn't respond - it's probably not running.
-> Try restarting this terminal window to bring resh-daemon back up -> Try restarting this terminal window to bring resh-daemon back up
-> If the problem persists you can check resh-daemon logs: ~/.local/share/resh/log.json (or ~/$XDG_DATA_HOME/resh/log.json) -> If the problem persists you can check resh-daemon logs: ~/.local/share/resh/log.json (or ~/$XDG_DATA_HOME/resh/log.json)
@ -51,12 +56,12 @@ It looks like you updated resh and didn't restart this terminal.
` `
func (f *Output) ErrorDaemonNotRunning(err error) { func (f *Output) ErrorDaemonNotRunning(err error) {
fmt.Fprintf(os.Stderr, "%s: %s", f.ErrPrefix, msgDeamonNotRunning) fmt.Fprintf(os.Stderr, "%s: %s", f.ErrPrefix, msgDaemonNotRunning)
f.Logger.Error("Daemon is not running", zap.Error(err)) f.Logger.Error("Daemon is not running", zap.Error(err))
} }
func (f *Output) FatalDaemonNotRunning(err error) { func (f *Output) FatalDaemonNotRunning(err error) {
fmt.Fprintf(os.Stderr, "%s: %s", f.ErrPrefix, msgDeamonNotRunning) fmt.Fprintf(os.Stderr, "%s: %s", f.ErrPrefix, msgDaemonNotRunning)
f.Logger.Fatal("Daemon is not running", zap.Error(err)) f.Logger.Fatal("Daemon is not running", zap.Error(err))
} }

@ -8,6 +8,7 @@ import (
"os" "os"
"strings" "strings"
"github.com/curusarn/resh/internal/futil"
"github.com/curusarn/resh/internal/recconv" "github.com/curusarn/resh/internal/recconv"
"github.com/curusarn/resh/internal/recordint" "github.com/curusarn/resh/internal/recordint"
"github.com/curusarn/resh/record" "github.com/curusarn/resh/record"
@ -34,8 +35,8 @@ func (r *RecIO) ReadAndFixFile(fpath string, maxErrors int) ([]recordint.Indexed
r.sugar.Infow("Backing up current corrupted history file", r.sugar.Infow("Backing up current corrupted history file",
"backupFilename", fpathBak, "backupFilename", fpathBak,
) )
// TODO: maybe use upstram copy function // TODO: maybe use upstream copy function
err = copyFile(fpath, fpathBak) err = futil.CopyFile(fpath, fpathBak)
if err != nil { if err != nil {
r.sugar.Errorw("Failed to create a backup history file - aborting fixing history file", r.sugar.Errorw("Failed to create a backup history file - aborting fixing history file",
"backupFilename", fpathBak, "backupFilename", fpathBak,
@ -99,31 +100,10 @@ func (r *RecIO) ReadFile(fpath string) ([]recordint.Indexed, int, error) {
return recs, numErrs, nil return recs, numErrs, nil
} }
func copyFile(source, dest string) error {
from, err := os.Open(source)
if err != nil {
return err
}
defer from.Close()
// This is equivalnet to: os.OpenFile(dest, os.O_RDWR|os.O_CREATE, 0666)
to, err := os.Create(dest)
if err != nil {
return err
}
defer to.Close()
_, err = io.Copy(to, from)
if err != nil {
return err
}
return nil
}
func (r *RecIO) decodeLine(line string) (*record.V1, error) { func (r *RecIO) decodeLine(line string) (*record.V1, error) {
idx := strings.Index(line, "{") idx := strings.Index(line, "{")
if idx == -1 { if idx == -1 {
return nil, fmt.Errorf("no openning brace found") return nil, fmt.Errorf("no opening brace found")
} }
schema := line[:idx] schema := line[:idx]
jsn := line[idx:] jsn := line[idx:]

@ -30,6 +30,7 @@ func (r *RecIO) AppendToFile(fpath string, recs []record.V1) error {
} }
// TODO: better errors // TODO: better errors
// TODO: rethink this
func (r *RecIO) EditRecordFlagsInFile(fpath string, idx int, rec recordint.Flag) error { func (r *RecIO) EditRecordFlagsInFile(fpath string, idx int, rec recordint.Flag) error {
// FIXME: implement // FIXME: implement
// open file "not as append" // open file "not as append"

@ -2,6 +2,8 @@ package recordint
import "github.com/curusarn/resh/record" import "github.com/curusarn/resh/record"
// TODO: rethink this
// Indexed record allows us to find records in history file in order to edit them // Indexed record allows us to find records in history file in order to edit them
type Indexed struct { type Indexed struct {
Rec record.V1 Rec record.V1

@ -1,7 +1,7 @@
#!/hint/sh #!/hint/sh
__resh_reset_variables() { __resh_reset_variables() {
__RESH_RECORD_ID=$(__resh_get_uuid) __RESH_RECORD_ID=$(resh-generate-uuid)
} }
__resh_preexec() { __resh_preexec() {
@ -43,8 +43,6 @@ __resh_collect() {
resh-collect -requireVersion "$__RESH_VERSION" \ resh-collect -requireVersion "$__RESH_VERSION" \
-requireRevision "$__RESH_REVISION" \ -requireRevision "$__RESH_REVISION" \
-shell "$__RESH_SHELL" \ -shell "$__RESH_SHELL" \
-device "$__RESH_HOST" \
-deviceID "$(cat ~/.resh/resh-uuid 2>/dev/null)" \
-sessionID "$__RESH_SESSION_ID" \ -sessionID "$__RESH_SESSION_ID" \
-recordID "$__RESH_RECORD_ID" \ -recordID "$__RESH_RECORD_ID" \
-home "$__RESH_HOME" \ -home "$__RESH_HOME" \

@ -64,51 +64,27 @@ else
fi fi
if [ "$(uname)" = Darwin ]; then if [ "$(uname)" = Darwin ] && gnohup --version >/dev/null 2>&1; then
if gnohup --version >/dev/null 2>&1; then echo " * Nohup installed: OK"
elif nohup --version >/dev/null 2>&1; then
echo " * Nohup installed: OK" echo " * Nohup installed: OK"
else else
echo " * Nohup installed: NOT INSTALLED!" echo " * Nohup installed: NOT INSTALLED!"
echo " > You don't have nohup" echo " > You don't have nohup"
echo " > Please install GNU coreutils" echo " > Please install GNU coreutils"
echo echo
if [ "$(uname)" = Darwin ]; then
echo " $ brew install coreutils" echo " $ brew install coreutils"
echo echo
exit 1
fi fi
else
if setsid --version >/dev/null 2>&1; then
echo " * Setsid installed: OK"
else
echo " * Setsid installed: NOT INSTALLED!"
echo " > You don't have setsid"
echo " > Please install unix-util"
echo
exit 1 exit 1
fi fi
fi
# echo # echo
# echo "Continue with installation? (Any key to CONTINUE / Ctrl+C to ABORT)" # echo "Continue with installation? (Any key to CONTINUE / Ctrl+C to ABORT)"
# # shellcheck disable=2034 # # shellcheck disable=2034
# read -r x # read -r x
echo "Backing up previous installation"
#./bin/resh-install-utils backup
# TODO: ~/.resh -> XDG_DATA_HOME/resh/rollback/
# TODO: ~/XDG_DATA_HOME/resh/history.reshjson -> XDG_DATA/resh/rollback/
# TODO: what about legacy history locations
# TODO: ~/XDG_DATA_HOME/resh/log.json -> XDG_DATA/resh/rollback/
echo "Cleaning up installation directory ..."
rm ~/.resh/bin/* 2>/dev/null ||:
rm ~/.resh/* 2>/dev/null ||:
# TODO: put this behind version condition
# backward compatibility: We have a new location for resh history file
[ ! -f ~/.resh/history.json ] || mv ~/.resh/history.json ~/.resh_history.json
#[ ! -f ~/.resh_history.json ] || mv ~/.resh_history.json $XDG .resh_history.json
echo "Creating directories ..." echo "Creating directories ..."
mkdir_if_not_exists() { mkdir_if_not_exists() {
@ -129,13 +105,19 @@ cp -f scripts/shellrc.sh ~/.resh/shellrc
cp -f scripts/reshctl.sh scripts/widgets.sh scripts/hooks.sh scripts/util.sh ~/.resh/ cp -f scripts/reshctl.sh scripts/widgets.sh scripts/hooks.sh scripts/util.sh ~/.resh/
cp -f scripts/rawinstall.sh ~/.resh/ cp -f scripts/rawinstall.sh ~/.resh/
# Copy all executables. We don't really need to omit install-utils from the bin directory
echo "Copying more files ..." echo "Copying more files ..."
cp -f scripts/uuid.sh ~/.resh/bin/resh-uuid cp -f bin/resh-* ~/.resh/bin/
cp -f bin/resh-{daemon,cli,control,collect,postcollect,session-init,config} ~/.resh/bin/
echo "Creating/updating config file ..." echo "Creating/updating config file ..."
./bin/resh-install-utils migrate-config ./bin/resh-install-utils migrate-config
echo "Checking/setting up device files ..."
./bin/resh-install-utils setup-device
echo "Updating format of history file ..."
./bin/resh-install-utils migrate-history
echo "Finishing up ..." echo "Finishing up ..."
# Adding resh shellrc to .bashrc ... # Adding resh shellrc to .bashrc ...
if [ ! -f ~/.bashrc ]; then if [ ! -f ~/.bashrc ]; then
@ -155,15 +137,6 @@ fi
# Deleting zsh completion cache - for future use # Deleting zsh completion cache - for future use
# [ ! -e ~/.zcompdump ] || rm ~/.zcompdump # [ ! -e ~/.zcompdump ] || rm ~/.zcompdump
# Final touch
# TODO: change
touch ~/.resh_history.json
# Generating resh-uuid ...
[ -e ~/.resh/resh-uuid ] \
|| cat /proc/sys/kernel/random/uuid > ~/.resh/resh-uuid 2>/dev/null \
|| scripts/uuid.sh > ~/.resh/resh-uuid 2>/dev/null
# Source utils to get __resh_run_daemon function # Source utils to get __resh_run_daemon function
# shellcheck source=util.sh # shellcheck source=util.sh
. ~/.resh/util.sh . ~/.resh/util.sh
@ -197,7 +170,7 @@ RESH SEARCH APPLICATION = Redesigned reverse search that actually works
(you will need to restart your terminal first) (you will need to restart your terminal first)
Search your history by commands. Search your history by commands.
Host, directories, git remote, and exit status is used to display relevant results first. Device, directories, git remote, and exit status is used to display relevant results first.
At first, the search application will use the standard shell history without context. At first, the search application will use the standard shell history without context.
All history recorded from now on will have context which will be used by the RESH SEARCH app. All history recorded from now on will have context which will be used by the RESH SEARCH app.
@ -207,9 +180,9 @@ CHECK FOR UPDATES
$ reshctl update $ reshctl update
HISTORY HISTORY
Your resh history will be recorded to '${XDG_DATA_HOME-~/.local/share}/resh/history/<device>.reshjson' Your resh history will be recorded to '${XDG_DATA_HOME-~/.local/share}/resh/history.reshjson'
Look at it using e.g. following command (you might need to install jq) Look at it using e.g. following command (you might need to install jq)
$ cat ${XDG_DATA_HOME-~/.local/share}/resh/history/<device>.reshjson | sed 's/^v[^{]*{/{/' | jq . $ cat ${XDG_DATA_HOME-~/.local/share}/resh/history.reshjson | sed 's/^v[^{]*{/{/' | jq .
ISSUES & FEEDBACK ISSUES & FEEDBACK
Please report issues to: https://github.com/curusarn/resh/issues Please report issues to: https://github.com/curusarn/resh/issues
@ -226,13 +199,12 @@ fi
info="$info info="$info
---- Close this by pressing Q ----" ---- Close this by pressing Q ----"
printf "%s\n" "$info" | ${PAGER:-less}
echo "$info" | ${PAGER:-less}
echo echo
echo "All done!" echo "All done!"
echo "Thank you for using RESH" echo "Thank you for using RESH"
echo "Issues go here: https://github.com/curusarn/resh/issues" echo "Report issues here: https://github.com/curusarn/resh/issues"
echo "Ctrl+R launches the RESH SEARCH app" echo "Ctrl+R launches the RESH SEARCH app"
if [ -z "${__RESH_VERSION:-}" ]; then echo " if [ -z "${__RESH_VERSION:-}" ]; then echo "

@ -62,7 +62,7 @@ __resh_unbind_all() {
resh() { resh() {
local buffer local buffer
local git_remote; git_remote="$(git remote get-url origin 2>/dev/null)" local git_remote; git_remote="$(git remote get-url origin 2>/dev/null)"
buffer=$(resh-cli --sessionID "$__RESH_SESSION_ID" --host "$__RESH_HOST" --pwd "$PWD" --gitOriginRemote "$git_remote" "$@") buffer=$(resh-cli --sessionID "$__RESH_SESSION_ID" --pwd "$PWD" --gitOriginRemote "$git_remote" "$@")
status_code=$? status_code=$?
if [ $status_code = 111 ]; then if [ $status_code = 111 ]; then
# execute # execute

@ -15,10 +15,8 @@ PATH=$PATH:~/.resh/bin
if [ -n "${ZSH_VERSION-}" ]; then if [ -n "${ZSH_VERSION-}" ]; then
# shellcheck disable=SC1009 # shellcheck disable=SC1009
__RESH_SHELL="zsh" __RESH_SHELL="zsh"
__RESH_HOST="$HOST"
elif [ -n "${BASH_VERSION-}" ]; then elif [ -n "${BASH_VERSION-}" ]; then
__RESH_SHELL="bash" __RESH_SHELL="bash"
__RESH_HOST="$HOSTNAME"
else else
echo "RESH PANIC: unrecognized shell - please report this to https://github.com/curusarn/resh/issues" echo "RESH PANIC: unrecognized shell - please report this to https://github.com/curusarn/resh/issues"
fi fi
@ -41,7 +39,7 @@ __resh_run_daemon
# NOTE: nested shells are still the same session # NOTE: nested shells are still the same session
# i.e. $__RESH_SESSION_ID will be set in nested shells # i.e. $__RESH_SESSION_ID will be set in nested shells
if [ -z "${__RESH_SESSION_ID+x}" ]; then if [ -z "${__RESH_SESSION_ID+x}" ]; then
export __RESH_SESSION_ID; __RESH_SESSION_ID=$(__resh_get_uuid) export __RESH_SESSION_ID; __RESH_SESSION_ID=$(resh-generate-uuid)
export __RESH_SESSION_PID="$$" export __RESH_SESSION_PID="$$"
__resh_reset_variables __resh_reset_variables

@ -1,9 +1,6 @@
#!/hint/sh #!/hint/sh
# util.sh - resh utility functions # util.sh - resh utility functions
__resh_get_uuid() {
cat /proc/sys/kernel/random/uuid 2>/dev/null || resh-uuid
}
__resh_get_pid() { __resh_get_pid() {
if [ -n "${ZSH_VERSION-}" ]; then if [ -n "${ZSH_VERSION-}" ]; then

@ -1,35 +0,0 @@
#!/usr/bin/env bash
# https://gist.github.com/markusfisch/6110640
# Generate a pseudo UUID
uuid()
{
local N B C='89ab'
for (( N=0; N < 16; ++N ))
do
B=$(( $RANDOM%256 ))
case $N in
6)
printf '4%x' $(( B%16 ))
;;
8)
printf '%c%x' ${C:$RANDOM%${#C}:1} $(( B%16 ))
;;
3 | 5 | 7 | 9)
printf '%02x-' $B
;;
*)
printf '%02x' $B
;;
esac
done
echo
}
if [ "$BASH_SOURCE" == "$0" ]
then
uuid
fi

@ -14,7 +14,7 @@ __resh_widget_control_R() {
local status_code local status_code
local git_remote; git_remote="$(git remote get-url origin 2>/dev/null)" local git_remote; git_remote="$(git remote get-url origin 2>/dev/null)"
BUFFER=$(resh-cli --sessionID "$__RESH_SESSION_ID" --host "$__RESH_HOST" --pwd "$PWD" --gitOriginRemote "$git_remote" --query "$BUFFER") BUFFER=$(resh-cli --sessionID "$__RESH_SESSION_ID" --pwd "$PWD" --gitOriginRemote "$git_remote" --query "$BUFFER")
status_code=$? status_code=$?
if [ $status_code = 111 ]; then if [ $status_code = 111 ]; then
# execute # execute

Loading…
Cancel
Save