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. 48
      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. 76
      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
- arm64
-
id: "config-setup"
main: ./cmd/config-setup
binary: bin/resh-config-setup
id: "install-utils"
main: ./cmd/install-utils
binary: bin/resh-install-utils
goarch:
- 386
- amd64
- arm
- arm64
-
id: "install-utils"
main: ./cmd/install-utils
binary: bin/resh-install-utils
id: "generate-uuid"
main: ./cmd/generate-uuid
binary: bin/resh-generate-uuid
goarch:
- 386
- 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\
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
scripts/install.sh
@ -22,11 +22,11 @@ rebuild:
make build
clean:
rm -f bin/*
rm -f -- bin/*
uninstall:
# Uninstalling ...
-rm -rf ~/.resh/
-rm -rf -- ~/.resh/
go_files = $(shell find -name '*.go')
bin/resh-%: $(go_files)

@ -16,6 +16,8 @@ import (
"github.com/awesome-gocui/gocui"
"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/msg"
"github.com/curusarn/resh/internal/output"
@ -29,14 +31,14 @@ import (
// info passed during build
var version string
var commit string
var developement bool
var development string
// special constant recognized by RESH wrappers
const exitCodeExecute = 111
func main() {
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
if errCfg != nil {
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) {
sessionID := flag.String("sessionID", "", "resh generated session id")
host := flag.String("host", "", "host")
pwd := flag.String("pwd", "", "present working directory")
gitOriginRemote := flag.String("gitOriginRemote", "DEFAULT", "git origin remote")
query := flag.String("query", "", "search query")
@ -62,15 +63,20 @@ func runReshCli(out *output.Output, config cfg.Config) (string, int) {
if *sessionID == "" {
out.Fatal(errMsg, errors.New("missing option --sessionId"))
}
if *host == "" {
out.Fatal(errMsg, errors.New("missing option --host"))
}
if *pwd == "" {
out.Fatal(errMsg, errors.New("missing option --pwd"))
}
if *gitOriginRemote == "DEFAULT" {
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)
if err != nil {
@ -100,11 +106,12 @@ func runReshCli(out *output.Output, config cfg.Config) (string, int) {
initialQuery: *query,
}
// TODO: Use device ID
layout := manager{
out: out,
config: config,
sessionID: *sessionID,
host: *host,
host: deviceName,
pwd: *pwd,
gitOriginRemote: *gitOriginRemote,
s: &st,

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

@ -14,11 +14,11 @@ import (
// info passed during build
var version string
var commit string
var developement bool
var development string
func main() {
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
if errCfg != nil {
logger.Error("Error while getting configuration", zap.Error(errCfg))

@ -7,10 +7,8 @@ import (
"github.com/spf13/cobra"
)
// info passed during build
var version string
var commit string
var developement bool
// globals
var config cfg.Config
@ -22,12 +20,12 @@ var rootCmd = &cobra.Command{
}
// Execute reshctl
func Execute(ver, com string) {
func Execute(ver, com, development string) {
version = ver
commit = com
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
out = output.New(logger, "ERROR")
if errCfg != nil {

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

@ -10,6 +10,8 @@ import (
"strings"
"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/logger"
"go.uber.org/zap"
@ -18,11 +20,11 @@ import (
// info passed during build
var version string
var commit string
var developement bool
var development string
func main() {
config, errCfg := cfg.New()
logger, err := logger.New("daemon", config.LogLevel, developement)
logger, err := logger.New("daemon", config.LogLevel, development)
if err != nil {
fmt.Printf("Error while creating logger: %v", err)
}
@ -32,48 +34,60 @@ func main() {
}
sugar := logger.Sugar()
d := daemon{sugar: sugar}
sugar.Infow("Deamon starting ...",
sugar.Infow("Daemon starting ...",
"version", version,
"commit", commit,
)
// TODO: rethink PID file and logs location
dataDir, err := datadir.MakePath()
if err != nil {
sugar.Fatalw("Could not get user data directory", zap.Error(err))
}
homeDir, err := os.UserHomeDir()
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")
reshHistoryPath := filepath.Join(homeDir, ".resh_history.json")
// TODO: These paths should be probably defined in a package
pidFile := filepath.Join(dataDir, "daemon.pid")
reshHistoryPath := filepath.Join(dataDir, "history.reshjson")
bashHistoryPath := filepath.Join(homeDir, ".bash_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()))
res, err := d.isDaemonRunning(config.Port)
if err != nil {
sugar.Errorw("Error while checking daemon status - "+
"it's probably not running", "error", err)
sugar.Errorw("Error while checking daemon status - it's probably not running",
"error", err)
}
if res {
sugar.Errorw("Daemon is already running - exiting!")
return
}
_, err = os.Stat(PIDFile)
_, err = os.Stat(pidFile)
if err == nil {
sugar.Warn("Pidfile exists")
sugar.Warnw("PID file exists",
"PIDFile", pidFile)
// kill daemon
err = d.killDaemon(PIDFile)
err = d.killDaemon(pidFile)
if err != nil {
sugar.Errorw("Could not kill daemon",
"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 {
sugar.Fatalw("Could not create pidfile",
sugar.Fatalw("Could not create PID file",
"error", err,
"PIDFile", PIDFile,
"PIDFile", pidFile,
)
}
server := Server{
@ -82,12 +96,15 @@ func main() {
reshHistoryPath: reshHistoryPath,
bashHistoryPath: bashHistoryPath,
zshHistoryPath: zshHistoryPath,
deviceID: deviceID,
deviceName: deviceName,
}
server.Run()
sugar.Infow("Removing PID file ...",
"PIDFile", PIDFile,
"PIDFile", pidFile,
)
err = os.Remove(PIDFile)
err = os.Remove(pidFile)
if err != nil {
sugar.Errorw("Could not delete PID file", "error", err)
}
@ -98,16 +115,6 @@ type daemon struct {
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) {
url := "http://localhost:" + strconv.Itoa(port) + "/status"
client := httpclient.New()
@ -119,14 +126,14 @@ func (d *daemon) isDaemonRunning(port int) (bool, error) {
return true, nil
}
func (d *daemon) killDaemon(pidfile string) error {
dat, err := ioutil.ReadFile(pidfile)
func (d *daemon) killDaemon(pidFile string) error {
dat, err := ioutil.ReadFile(pidFile)
if err != nil {
d.sugar.Errorw("Reading pid file failed",
"PIDFile", pidfile,
d.sugar.Errorw("Reading PID file failed",
"PIDFile", pidFile,
"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"))
if err != nil {
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 {
sugar *zap.SugaredLogger
subscribers []chan recordint.Collect
deviceID string
deviceName string
}
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,
"part", part,
)
rec.Rec.DeviceID = h.deviceID
rec.Rec.Device = h.deviceName
sugar.Debugw("Got record, sending to subscribers ...")
for _, sub := range h.subscribers {
sub <- rec

@ -23,6 +23,9 @@ type Server struct {
reshHistoryPath string
bashHistoryPath string
zshHistoryPath string
deviceID string
deviceName string
}
func (s *Server) Run() {
@ -63,7 +66,12 @@ func (s *Server) Run() {
// handlers
mux := http.NewServeMux()
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("/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 (
"fmt"
"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
var version string
var commit string
var developement bool
var development string
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 {
fmt.Fprintf(os.Stderr, "ERROR: Not eonugh arguments\n")
out.ErrorWOErr("ERROR: Not enough arguments\n")
printUsage(os.Stderr)
os.Exit(1)
}
command := os.Args[1]
switch command {
@ -24,27 +46,31 @@ func main() {
case "migrate-config":
migrateConfig()
case "migrate-history":
migrateHistory()
migrateHistory(out)
case "setup-device":
setupDevice()
case "help":
printUsage(os.Stdout)
default:
fmt.Fprintf(os.Stderr, "ERROR: Unknown command: %s\n", command)
out.ErrorWOErr(fmt.Sprintf("ERROR: Unknown command: %s\n", command))
printUsage(os.Stderr)
os.Exit(1)
}
}
func printUsage(f *os.File) {
usage := `
USAGE: ./install-utils COMMAND
Utils used during RESH instalation.
Utils used during RESH installation.
COMMANDS:
backup backup resh installation and data
rollback restore resh installation and data from backup
migrate-config update config to reflect updates
migrate-history update history to reflect updates
help show this help
backup backup resh installation and data
rollback restore resh installation and data from backup
migrate-config update config to latest format
migrate-history update history to latest format
setup-device setup device name and device ID
help show this help
`
fmt.Fprintf(f, usage)
fmt.Fprint(f, usage)
}

@ -3,8 +3,14 @@ package main
import (
"fmt"
"os"
"path"
"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() {
@ -23,18 +29,101 @@ func migrateConfig() {
}
}
func migrateHistory() {
// homeDir, err := os.UserHomeDir()
// if err != nil {
func migrateHistory(out *output.Output) {
migrateHistoryLocation(out)
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)
}
legacyHistoryPaths := []string{
path.Join(homeDir, ".resh_history.json"),
path.Join(homeDir, ".resh/history.json"),
}
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
}
}
}
// }
func migrateHistoryFormat(out *output.Output) {
dataDir, err := datadir.MakePath()
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)
}
// TODO: Find history in:
// - .resh/history.json (copy) - message user to delete the file once they confirm the new setup works
// - .resh_history.json (copy) - message user to delete the file once they confirm the new setup works
// - xdg_data/resh/history.reshjson
err = futil.CopyFile(historyPath, historyPathBak)
if err != nil {
out.Fatal("ERROR: Could not back up history file", err)
}
// Read xdg_data/resh/history.reshjson
// Write xdg_data/resh/history.reshjson
// the old format can be found in the backup dir
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
var version string
var commit string
var developement bool
var development string
func main() {
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
if errCfg != nil {
logger.Error("Error while getting configuration", zap.Error(errCfg))

@ -18,11 +18,11 @@ import (
// info passed during build
var version string
var commit string
var developement bool
var development string
func main() {
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
if errCfg != nil {
logger.Error("Error while getting configuration", zap.Error(errCfg))

@ -5,6 +5,8 @@ go 1.18
require (
github.com/BurntSushi/toml v0.4.1
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/spf13/cobra v1.2.1
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-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
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/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=
@ -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/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-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-runewidth v0.0.7/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"
"path"
"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) {
fname := "device-id"
dat, err := os.ReadFile(path.Join(dataDir, fname))
return readValue(dataDir, fnameID)
}
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 {
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")
return id, nil
val := strings.TrimRight(string(dat), "\n")
return val, nil
}
func GetName(dataDir string) (string, error) {
fname := "device-name"
dat, err := os.ReadFile(path.Join(dataDir, fname))
if err != nil {
return "", fmt.Errorf("could not read file with device-name: %w", err)
}
name := strings.TrimRight(string(dat), "\n")
return name, nil
}
// TODO: implement, possibly with a better name
// func CheckID(dataDir string) (string, error) {
// fname := "device-id"
// dat, err := os.ReadFile(path.Join(dataDir, fname))
// if err != nil {
// return "", fmt.Errorf("could not read file with device-id: %w", err)
// }
// id := strings.TrimRight(string(dat), "\n")
// return id, nil
// }
//
// func CheckName(dataDir string) (string, error) {
// fname := "device-id"
// dat, err := os.ReadFile(path.Join(dataDir, fname))
// if err != nil {
// return "", fmt.Errorf("could not read file with device-id: %w", err)
// }
// id := strings.TrimRight(string(dat), "\n")
// return id, nil
// }
func generateIDIfUnset(dataDir string) error {
fpath := path.Join(dataDir, fnameID)
exists, err := futil.FileExists(fpath)
if err != nil {
return err
}
if exists {
return nil
}
rnd, err := uuid.NewRandom()
if err != nil {
return fmt.Errorf("could not get new random source: %w", err)
}
id := rnd.String()
if id == "" {
return fmt.Errorf("got invalid UUID from package")
}
err = os.WriteFile(fpath, []byte(id), filePerm)
if err != nil {
return fmt.Errorf("could not write generated ID to file: %w", err)
}
return nil
}
func promptAndWriteNameIfUnset(dataDir string) error {
fpath := path.Join(dataDir, fnameName)
exists, err := futil.FileExists(fpath)
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"
)
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()
if err != nil {
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.OutputPaths = []string{logPath}
loggerConfig.Level.SetLevel(level)
loggerConfig.Development = developement // DPanic panics in developement
loggerConfig.Development = development == "true" // DPanic panics in development
logger, err := loggerConfig.Build()
if err != nil {
return logger, fmt.Errorf("error while creating logger: %w", err)

@ -7,7 +7,7 @@ import (
"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
type Output struct {
Logger *zap.Logger
@ -22,21 +22,26 @@ func New(logger *zap.Logger, prefix string) *Output {
}
func (f *Output) Info(msg string) {
fmt.Fprintf(os.Stdout, msg)
fmt.Printf("%s\n", msg)
f.Logger.Info(msg)
}
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))
}
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) {
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))
}
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
-> 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) {
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))
}
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))
}

@ -8,6 +8,7 @@ import (
"os"
"strings"
"github.com/curusarn/resh/internal/futil"
"github.com/curusarn/resh/internal/recconv"
"github.com/curusarn/resh/internal/recordint"
"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",
"backupFilename", fpathBak,
)
// TODO: maybe use upstram copy function
err = copyFile(fpath, fpathBak)
// TODO: maybe use upstream copy function
err = futil.CopyFile(fpath, fpathBak)
if err != nil {
r.sugar.Errorw("Failed to create a backup history file - aborting fixing history file",
"backupFilename", fpathBak,
@ -99,31 +100,10 @@ func (r *RecIO) ReadFile(fpath string) ([]recordint.Indexed, int, error) {
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) {
idx := strings.Index(line, "{")
if idx == -1 {
return nil, fmt.Errorf("no openning brace found")
return nil, fmt.Errorf("no opening brace found")
}
schema := line[:idx]
jsn := line[idx:]

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

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

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

@ -64,28 +64,20 @@ else
fi
if [ "$(uname)" = Darwin ]; then
if gnohup --version >/dev/null 2>&1; then
echo " * Nohup installed: OK"
else
echo " * Nohup installed: NOT INSTALLED!"
echo " > You don't have nohup"
echo " > Please install GNU coreutils"
echo
echo " $ brew install coreutils"
echo
exit 1
fi
if [ "$(uname)" = Darwin ] && gnohup --version >/dev/null 2>&1; then
echo " * Nohup installed: OK"
elif nohup --version >/dev/null 2>&1; then
echo " * Nohup installed: OK"
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 " * Nohup installed: NOT INSTALLED!"
echo " > You don't have nohup"
echo " > Please install GNU coreutils"
echo
if [ "$(uname)" = Darwin ]; then
echo " $ brew install coreutils"
echo
exit 1
fi
exit 1
fi
# echo
@ -93,22 +85,6 @@ fi
# # shellcheck disable=2034
# 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 ..."
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/rawinstall.sh ~/.resh/
# Copy all executables. We don't really need to omit install-utils from the bin directory
echo "Copying more files ..."
cp -f scripts/uuid.sh ~/.resh/bin/resh-uuid
cp -f bin/resh-{daemon,cli,control,collect,postcollect,session-init,config} ~/.resh/bin/
cp -f bin/resh-* ~/.resh/bin/
echo "Creating/updating config file ..."
./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 ..."
# Adding resh shellrc to .bashrc ...
if [ ! -f ~/.bashrc ]; then
@ -155,15 +137,6 @@ fi
# Deleting zsh completion cache - for future use
# [ ! -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
# shellcheck source=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)
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.
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
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)
$ 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
Please report issues to: https://github.com/curusarn/resh/issues
@ -226,13 +199,12 @@ fi
info="$info
---- Close this by pressing Q ----"
echo "$info" | ${PAGER:-less}
printf "%s\n" "$info" | ${PAGER:-less}
echo
echo "All done!"
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"
if [ -z "${__RESH_VERSION:-}" ]; then echo "

@ -62,7 +62,7 @@ __resh_unbind_all() {
resh() {
local buffer
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=$?
if [ $status_code = 111 ]; then
# execute

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

@ -1,9 +1,6 @@
#!/hint/sh
# util.sh - resh utility functions
__resh_get_uuid() {
cat /proc/sys/kernel/random/uuid 2>/dev/null || resh-uuid
}
__resh_get_pid() {
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 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=$?
if [ $status_code = 111 ]; then
# execute

Loading…
Cancel
Save