get resh to compile and run

pull/179/head
Simon Let 3 years ago
parent 9dfcb25d95
commit 0c6ce3c8e9
No known key found for this signature in database
GPG Key ID: D650C65DD46D431D
  1. 9
      .goreleaser.yml
  2. 2
      Makefile
  3. 10
      cmd/cli/main.go
  4. 166
      cmd/collect/main.go
  5. 2
      cmd/daemon/dump.go
  6. 8
      cmd/daemon/main.go
  7. 16
      cmd/daemon/record.go
  8. 12
      cmd/daemon/run-server.go
  9. 14
      cmd/daemon/session-init.go
  10. 4
      cmd/install-utils/main.go
  11. 111
      cmd/postcollect/main.go
  12. 143
      cmd/session-init/main.go
  13. 1
      go.mod
  14. 2
      go.sum
  15. 1
      internal/cfg/cfg.go
  16. 33
      internal/collect/collect.go
  17. 11
      internal/histcli/histcli.go
  18. 125
      internal/histfile/histfile.go
  19. 4
      internal/msg/msg.go
  20. 20
      internal/output/output.go
  21. 28
      internal/recconv/recconv.go
  22. 2
      internal/recio/read.go
  23. 34
      internal/recio/write.go
  24. 8
      internal/record/v1.go
  25. 13
      internal/recordint/collect.go
  26. 51
      internal/recordint/enriched.go
  27. 51
      internal/recordint/searchapp.go
  28. 256
      internal/records/records.go
  29. 85
      internal/records/records_test.go
  30. 27
      internal/records/testdata/resh_history.json
  31. 109
      internal/recutil/recutil.go
  32. 22
      internal/searchapp/item.go
  33. 12
      internal/searchapp/test.go
  34. 26
      internal/sesswatch/sesswatch.go
  35. 75
      scripts/hooks.sh
  36. 5
      scripts/install.sh
  37. 4
      scripts/reshctl.sh
  38. 3
      scripts/shellrc.sh
  39. 40
      scripts/util.sh
  40. 6
      scripts/widgets.sh

@ -102,6 +102,15 @@ builds:
- amd64 - amd64
- arm - arm
- arm64 - arm64
-
id: "install-utils"
main: ./cmd/install-utils
binary: bin/resh-install-utils
goarch:
- 386
- amd64
- arm
- arm64
# signs: # signs:
# - artifacts: checksum # - artifacts: checksum

@ -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/installutil bin/resh-install-utils
install: build install: build
scripts/install.sh scripts/install.sh

@ -19,7 +19,7 @@ import (
"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"
"github.com/curusarn/resh/internal/records" "github.com/curusarn/resh/internal/recordint"
"github.com/curusarn/resh/internal/searchapp" "github.com/curusarn/resh/internal/searchapp"
"go.uber.org/zap" "go.uber.org/zap"
@ -96,7 +96,7 @@ func runReshCli(out *output.Output, config cfg.Config) (string, int) {
st := state{ st := state{
// lock sync.Mutex // lock sync.Mutex
cliRecords: resp.CliRecords, cliRecords: resp.Records,
initialQuery: *query, initialQuery: *query,
} }
@ -106,7 +106,7 @@ func runReshCli(out *output.Output, config cfg.Config) (string, int) {
sessionID: *sessionID, sessionID: *sessionID,
host: *host, host: *host,
pwd: *pwd, pwd: *pwd,
gitOriginRemote: records.NormalizeGitRemote(*gitOriginRemote), gitOriginRemote: *gitOriginRemote,
s: &st, s: &st,
} }
g.SetManager(layout) g.SetManager(layout)
@ -159,7 +159,7 @@ func runReshCli(out *output.Output, config cfg.Config) (string, int) {
type state struct { type state struct {
lock sync.Mutex lock sync.Mutex
cliRecords []records.CliRecord cliRecords []recordint.SearchApp
data []searchapp.Item data []searchapp.Item
rawData []searchapp.RawItem rawData []searchapp.RawItem
highlightedItem int highlightedItem int
@ -600,7 +600,7 @@ func SendCliMsg(out *output.Output, m msg.CliMsg, port string) msg.CliResponse {
out.Fatal("Failed decode response", err) out.Fatal("Failed decode response", err)
} }
sugar.Debugw("Recieved records from daemon", sugar.Debugw("Recieved records from daemon",
"recordCount", len(response.CliRecords), "recordCount", len(response.Records),
) )
return response return response
} }

@ -9,7 +9,8 @@ import (
"github.com/curusarn/resh/internal/collect" "github.com/curusarn/resh/internal/collect"
"github.com/curusarn/resh/internal/logger" "github.com/curusarn/resh/internal/logger"
"github.com/curusarn/resh/internal/output" "github.com/curusarn/resh/internal/output"
"github.com/curusarn/resh/internal/records" "github.com/curusarn/resh/internal/record"
"github.com/curusarn/resh/internal/recordint"
"go.uber.org/zap" "go.uber.org/zap"
// "os/exec" // "os/exec"
@ -32,14 +33,6 @@ func main() {
} }
out := output.New(logger, "resh-collect ERROR") out := output.New(logger, "resh-collect ERROR")
homeDir, err := os.UserHomeDir()
if err != nil {
out.Fatal("Could not get user home dir", err)
}
reshUUIDPath := filepath.Join(homeDir, "/.resh/resh-uuid")
machineIDPath := "/etc/machine-id"
// version // version
showVersion := flag.Bool("version", false, "Show version and exit") showVersion := flag.Bool("version", false, "Show version and exit")
showRevision := flag.Bool("revision", false, "Show git revision and exit") showRevision := flag.Bool("revision", false, "Show git revision and exit")
@ -49,55 +42,27 @@ func main() {
// core // core
cmdLine := flag.String("cmdLine", "", "command line") cmdLine := flag.String("cmdLine", "", "command line")
exitCode := flag.Int("exitCode", -1, "exit code")
shell := flag.String("shell", "", "actual shell")
uname := flag.String("uname", "", "uname")
sessionID := flag.String("sessionId", "", "resh generated session id")
recordID := flag.String("recordId", "", "resh generated record id")
// posix variables
cols := flag.String("cols", "-1", "$COLUMNS")
lines := flag.String("lines", "-1", "$LINES")
home := flag.String("home", "", "$HOME") home := flag.String("home", "", "$HOME")
lang := flag.String("lang", "", "$LANG")
lcAll := flag.String("lcAll", "", "$LC_ALL")
login := flag.String("login", "", "$LOGIN")
// path := flag.String("path", "", "$PATH")
pwd := flag.String("pwd", "", "$PWD - present working directory") pwd := flag.String("pwd", "", "$PWD - present working directory")
shellEnv := flag.String("shellEnv", "", "$SHELL")
term := flag.String("term", "", "$TERM") // 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")
hostname := flag.String("hostname", "", "$HOSTNAME")
// non-posix // non-posix
pid := flag.Int("pid", -1, "$$")
sessionPid := flag.Int("sessionPid", -1, "$$ at session start")
shlvl := flag.Int("shlvl", -1, "$SHLVL") shlvl := flag.Int("shlvl", -1, "$SHLVL")
host := flag.String("host", "", "$HOSTNAME")
hosttype := flag.String("hosttype", "", "$HOSTTYPE")
ostype := flag.String("ostype", "", "$OSTYPE")
machtype := flag.String("machtype", "", "$MACHTYPE")
gitCdup := flag.String("gitCdup", "", "git rev-parse --show-cdup")
gitRemote := flag.String("gitRemote", "", "git remote get-url origin") gitRemote := flag.String("gitRemote", "", "git remote get-url origin")
gitCdupExitCode := flag.Int("gitCdupExitCode", -1, "... $?") time_ := flag.String("time", "-1", "$EPOCHREALTIME")
gitRemoteExitCode := flag.Int("gitRemoteExitCode", -1, "... $?")
// before after
timezoneBefore := flag.String("timezoneBefore", "", "")
osReleaseID := flag.String("osReleaseId", "", "/etc/os-release ID")
osReleaseVersionID := flag.String("osReleaseVersionId", "",
"/etc/os-release ID")
osReleaseIDLike := flag.String("osReleaseIdLike", "", "/etc/os-release ID")
osReleaseName := flag.String("osReleaseName", "", "/etc/os-release ID")
osReleasePrettyName := flag.String("osReleasePrettyName", "",
"/etc/os-release ID")
rtb := flag.String("realtimeBefore", "-1", "before $EPOCHREALTIME")
rtsess := flag.String("realtimeSession", "-1",
"on session start $EPOCHREALTIME")
rtsessboot := flag.String("realtimeSessSinceBoot", "-1",
"on session start $EPOCHREALTIME")
flag.Parse() flag.Parse()
if *showVersion == true { if *showVersion == true {
@ -109,37 +74,17 @@ func main() {
os.Exit(0) os.Exit(0)
} }
if *requireVersion != "" && *requireVersion != version { if *requireVersion != "" && *requireVersion != version {
fmt.Println("Please restart/reload this terminal session " + out.FatalVersionMismatch(version, *requireVersion)
"(resh version: " + version +
"; resh version of this terminal session: " + *requireVersion +
")")
os.Exit(3)
} }
if *requireRevision != "" && *requireRevision != commit { if *requireRevision != "" && *requireRevision != commit {
fmt.Println("Please restart/reload this terminal session " + // this is only relevant for dev versions so we can reuse FatalVersionMismatch()
"(resh revision: " + commit + out.FatalVersionMismatch("revision "+commit, "revision "+*requireVersion)
"; resh revision of this terminal session: " + *requireRevision +
")")
os.Exit(3)
} }
realtimeBefore, err := strconv.ParseFloat(*rtb, 64) time, err := strconv.ParseFloat(*time_, 64)
if err != nil {
out.Fatal("Error while parsing flag --realtimeBefore", err)
}
realtimeSessionStart, err := strconv.ParseFloat(*rtsess, 64)
if err != nil { if err != nil {
out.Fatal("Error while parsing flag --realtimeSession", err) out.Fatal("Error while parsing flag --time", err)
} }
realtimeSessSinceBoot, err := strconv.ParseFloat(*rtsessboot, 64)
if err != nil {
out.Fatal("Error while parsing flag --realtimeSessSinceBoot", err)
}
realtimeSinceSessionStart := realtimeBefore - realtimeSessionStart
realtimeSinceBoot := realtimeSessSinceBoot + realtimeSinceSessionStart
timezoneBeforeOffset := collect.GetTimezoneOffsetInSeconds(logger, *timezoneBefore)
realtimeBeforeLocal := realtimeBefore + timezoneBeforeOffset
realPwd, err := filepath.EvalSymlinks(*pwd) realPwd, err := filepath.EvalSymlinks(*pwd)
if err != nil { if err != nil {
@ -147,79 +92,34 @@ func main() {
realPwd = "" realPwd = ""
} }
gitDir, gitRealDir := collect.GetGitDirs(logger, *gitCdup, *gitCdupExitCode, *pwd) rec := recordint.Collect{
if *gitRemoteExitCode != 0 { SessionID: *sessionID,
*gitRemote = "" Shlvl: *shlvl,
} SessionPID: *sessionPID,
// if *osReleaseID == "" {
// *osReleaseID = "linux"
// }
// if *osReleaseName == "" {
// *osReleaseName = "Linux"
// }
// if *osReleasePrettyName == "" {
// *osReleasePrettyName = "Linux"
// }
rec := records.Record{
// posix
Cols: *cols,
Lines: *lines,
// core
BaseRecord: records.BaseRecord{
CmdLine: *cmdLine,
ExitCode: *exitCode,
Shell: *shell, Shell: *shell,
Uname: *uname,
Rec: record.V1{
DeviceID: *deviceID,
SessionID: *sessionID, SessionID: *sessionID,
RecordID: *recordID, RecordID: *recordID,
CmdLine: *cmdLine,
// posix // posix
Home: *home, Home: *home,
Lang: *lang,
LcAll: *lcAll,
Login: *login,
// Path: *path,
Pwd: *pwd, Pwd: *pwd,
ShellEnv: *shellEnv,
Term: *term,
// non-posix
RealPwd: realPwd, RealPwd: realPwd,
Pid: *pid,
SessionPID: *sessionPid,
Host: *host,
Hosttype: *hosttype,
Ostype: *ostype,
Machtype: *machtype,
Shlvl: *shlvl,
// before after Logname: *logname,
TimezoneBefore: *timezoneBefore, Hostname: *hostname,
RealtimeBefore: realtimeBefore,
RealtimeBeforeLocal: realtimeBeforeLocal,
RealtimeSinceSessionStart: realtimeSinceSessionStart,
RealtimeSinceBoot: realtimeSinceBoot,
GitDir: gitDir,
GitRealDir: gitRealDir,
GitOriginRemote: *gitRemote, GitOriginRemote: *gitRemote,
MachineID: collect.ReadFileContent(out.Logger, machineIDPath),
OsReleaseID: *osReleaseID, Time: time,
OsReleaseVersionID: *osReleaseVersionID,
OsReleaseIDLike: *osReleaseIDLike,
OsReleaseName: *osReleaseName,
OsReleasePrettyName: *osReleasePrettyName,
PartOne: true, PartOne: true,
PartsNotMerged: true,
ReshUUID: collect.ReadFileContent(out.Logger, reshUUIDPath),
ReshVersion: version,
ReshRevision: commit,
}, },
} }
collect.SendRecord(out, rec, strconv.Itoa(config.Port), "/record") collect.SendRecord(out, rec, strconv.Itoa(config.Port), "/record")

@ -40,7 +40,7 @@ func (h *dumpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
sugar.Errorw("Error when getting records", "error", err) sugar.Errorw("Error when getting records", "error", err)
} }
resp := msg.CliResponse{CliRecords: fullRecords.List} resp := msg.CliResponse{Records: fullRecords.List}
jsn, err = json.Marshal(&resp) jsn, err = json.Marshal(&resp)
if err != nil { if err != nil {
sugar.Errorw("Error when marshaling", "error", err) sugar.Errorw("Error when marshaling", "error", err)

@ -22,7 +22,10 @@ var developement bool
func main() { func main() {
config, errCfg := cfg.New() config, errCfg := cfg.New()
logger, _ := logger.New("daemon", config.LogLevel, developement) logger, err := logger.New("daemon", config.LogLevel, developement)
if err != nil {
fmt.Printf("Error while creating logger: %v", err)
}
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))
@ -34,9 +37,6 @@ func main() {
"commit", commit, "commit", commit,
) )
// xdgCacheHome := d.getEnvOrPanic("__RESH_XDG_CACHE_HOME")
// xdgDataHome := d.getEnvOrPanic("__RESH_XDG_DATA_HOME")
// TODO: rethink PID file and logs location // TODO: rethink PID file and logs location
homeDir, err := os.UserHomeDir() homeDir, err := os.UserHomeDir()
if err != nil { if err != nil {

@ -5,11 +5,11 @@ import (
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"github.com/curusarn/resh/internal/records" "github.com/curusarn/resh/internal/recordint"
"go.uber.org/zap" "go.uber.org/zap"
) )
func NewRecordHandler(sugar *zap.SugaredLogger, subscribers []chan records.Record) recordHandler { func NewRecordHandler(sugar *zap.SugaredLogger, subscribers []chan recordint.Collect) recordHandler {
return recordHandler{ return recordHandler{
sugar: sugar.With(zap.String("endpoint", "/record")), sugar: sugar.With(zap.String("endpoint", "/record")),
subscribers: subscribers, subscribers: subscribers,
@ -18,7 +18,7 @@ func NewRecordHandler(sugar *zap.SugaredLogger, subscribers []chan records.Recor
type recordHandler struct { type recordHandler struct {
sugar *zap.SugaredLogger sugar *zap.SugaredLogger
subscribers []chan records.Record subscribers []chan recordint.Collect
} }
func (h *recordHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (h *recordHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
@ -34,8 +34,8 @@ func (h *recordHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} }
sugar.Debugw("Unmarshaling record ...") sugar.Debugw("Unmarshaling record ...")
record := records.Record{} rec := recordint.Collect{}
err = json.Unmarshal(jsn, &record) err = json.Unmarshal(jsn, &rec)
if err != nil { if err != nil {
sugar.Errorw("Error during unmarshaling", sugar.Errorw("Error during unmarshaling",
"error", err, "error", err,
@ -44,16 +44,16 @@ func (h *recordHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return return
} }
part := "2" part := "2"
if record.PartOne { if rec.Rec.PartOne {
part = "1" part = "1"
} }
sugar := sugar.With( sugar := sugar.With(
"cmdLine", record.CmdLine, "cmdLine", rec.Rec.CmdLine,
"part", part, "part", part,
) )
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 <- record sub <- rec
} }
sugar.Debugw("Record sent to subscribers") sugar.Debugw("Record sent to subscribers")
}() }()

@ -8,7 +8,7 @@ import (
"github.com/curusarn/resh/internal/cfg" "github.com/curusarn/resh/internal/cfg"
"github.com/curusarn/resh/internal/histfile" "github.com/curusarn/resh/internal/histfile"
"github.com/curusarn/resh/internal/records" "github.com/curusarn/resh/internal/recordint"
"github.com/curusarn/resh/internal/sesswatch" "github.com/curusarn/resh/internal/sesswatch"
"github.com/curusarn/resh/internal/signalhandler" "github.com/curusarn/resh/internal/signalhandler"
"go.uber.org/zap" "go.uber.org/zap"
@ -26,15 +26,15 @@ type Server struct {
} }
func (s *Server) Run() { func (s *Server) Run() {
var recordSubscribers []chan records.Record var recordSubscribers []chan recordint.Collect
var sessionInitSubscribers []chan records.Record var sessionInitSubscribers []chan recordint.SessionInit
var sessionDropSubscribers []chan string var sessionDropSubscribers []chan string
var signalSubscribers []chan os.Signal var signalSubscribers []chan os.Signal
shutdown := make(chan string) shutdown := make(chan string)
// histfile // histfile
histfileRecords := make(chan records.Record) histfileRecords := make(chan recordint.Collect)
recordSubscribers = append(recordSubscribers, histfileRecords) recordSubscribers = append(recordSubscribers, histfileRecords)
histfileSessionsToDrop := make(chan string) histfileSessionsToDrop := make(chan string)
sessionDropSubscribers = append(sessionDropSubscribers, histfileSessionsToDrop) sessionDropSubscribers = append(sessionDropSubscribers, histfileSessionsToDrop)
@ -48,9 +48,9 @@ func (s *Server) Run() {
histfileSignals, shutdown) histfileSignals, shutdown)
// sesswatch // sesswatch
sesswatchRecords := make(chan records.Record) sesswatchRecords := make(chan recordint.Collect)
recordSubscribers = append(recordSubscribers, sesswatchRecords) recordSubscribers = append(recordSubscribers, sesswatchRecords)
sesswatchSessionsToWatch := make(chan records.Record) sesswatchSessionsToWatch := make(chan recordint.SessionInit)
sessionInitSubscribers = append(sessionInitSubscribers, sesswatchSessionsToWatch) sessionInitSubscribers = append(sessionInitSubscribers, sesswatchSessionsToWatch)
sesswatch.Go( sesswatch.Go(
s.sugar, s.sugar,

@ -5,13 +5,13 @@ import (
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"github.com/curusarn/resh/internal/records" "github.com/curusarn/resh/internal/recordint"
"go.uber.org/zap" "go.uber.org/zap"
) )
type sessionInitHandler struct { type sessionInitHandler struct {
sugar *zap.SugaredLogger sugar *zap.SugaredLogger
subscribers []chan records.Record subscribers []chan recordint.SessionInit
} }
func (h *sessionInitHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (h *sessionInitHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
@ -28,8 +28,8 @@ func (h *sessionInitHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} }
sugar.Debugw("Unmarshaling record ...") sugar.Debugw("Unmarshaling record ...")
record := records.Record{} rec := recordint.SessionInit{}
err = json.Unmarshal(jsn, &record) err = json.Unmarshal(jsn, &rec)
if err != nil { if err != nil {
sugar.Errorw("Error during unmarshaling", sugar.Errorw("Error during unmarshaling",
"error", err, "error", err,
@ -38,12 +38,12 @@ func (h *sessionInitHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return return
} }
sugar := sugar.With( sugar := sugar.With(
"sessionID", record.SessionID, "sessionID", rec.SessionID,
"sessionPID", record.SessionPID, "sessionPID", rec.SessionPID,
) )
sugar.Infow("Got session, sending to subscribers ...") sugar.Infow("Got session, sending to subscribers ...")
for _, sub := range h.subscribers { for _, sub := range h.subscribers {
sub <- record sub <- rec
} }
sugar.Debugw("Session sent to subscribers") sugar.Debugw("Session sent to subscribers")
}() }()

@ -20,7 +20,9 @@ func main() {
case "backup": case "backup":
backup() backup()
case "rollback": case "rollback":
rollback() // FIXME
panic("Rollback not implemented yet!")
// rollback()
case "migrate-config": case "migrate-config":
migrateConfig() migrateConfig()
case "migrate-history": case "migrate-history":

@ -9,12 +9,12 @@ import (
"github.com/curusarn/resh/internal/collect" "github.com/curusarn/resh/internal/collect"
"github.com/curusarn/resh/internal/logger" "github.com/curusarn/resh/internal/logger"
"github.com/curusarn/resh/internal/output" "github.com/curusarn/resh/internal/output"
"github.com/curusarn/resh/internal/records" "github.com/curusarn/resh/internal/record"
"github.com/curusarn/resh/internal/recordint"
"go.uber.org/zap" "go.uber.org/zap"
// "os/exec" // "os/exec"
"path/filepath"
"strconv" "strconv"
) )
@ -32,44 +32,20 @@ func main() {
} }
out := output.New(logger, "resh-postcollect ERROR") out := output.New(logger, "resh-postcollect ERROR")
homeDir, err := os.UserHomeDir()
if err != nil {
out.Fatal("Could not get user home dir", err)
}
reshUUIDPath := filepath.Join(homeDir, "/.resh/resh-uuid")
machineIDPath := "/etc/machine-id"
showVersion := flag.Bool("version", false, "Show version and exit") showVersion := flag.Bool("version", false, "Show version and exit")
showRevision := flag.Bool("revision", false, "Show git revision and exit") showRevision := flag.Bool("revision", false, "Show git revision and exit")
requireVersion := flag.String("requireVersion", "", "abort if version doesn't match") requireVersion := flag.String("requireVersion", "", "abort if version doesn't match")
requireRevision := flag.String("requireRevision", "", "abort if revision doesn't match") requireRevision := flag.String("requireRevision", "", "abort if revision doesn't match")
cmdLine := flag.String("cmdLine", "", "command line")
exitCode := flag.Int("exitCode", -1, "exit code") exitCode := flag.Int("exitCode", -1, "exit code")
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")
shlvl := flag.Int("shlvl", -1, "$SHLVL") shlvl := flag.Int("shlvl", -1, "$SHLVL")
shell := flag.String("shell", "", "actual shell")
// posix variables
pwdAfter := flag.String("pwdAfter", "", "$PWD after command")
// non-posix
// sessionPid := flag.Int("sessionPid", -1, "$$ at session start")
gitCdupAfter := flag.String("gitCdupAfter", "", "git rev-parse --show-cdup")
gitRemoteAfter := flag.String("gitRemoteAfter", "", "git remote get-url origin")
gitCdupExitCodeAfter := flag.Int("gitCdupExitCodeAfter", -1, "... $?")
gitRemoteExitCodeAfter := flag.Int("gitRemoteExitCodeAfter", -1, "... $?")
// before after rtb := flag.String("timeBefore", "-1", "before $EPOCHREALTIME")
timezoneAfter := flag.String("timezoneAfter", "", "") rta := flag.String("timeAfter", "-1", "after $EPOCHREALTIME")
rtb := flag.String("realtimeBefore", "-1", "before $EPOCHREALTIME")
rta := flag.String("realtimeAfter", "-1", "after $EPOCHREALTIME")
flag.Parse() flag.Parse()
if *showVersion == true { if *showVersion == true {
@ -81,77 +57,36 @@ func main() {
os.Exit(0) os.Exit(0)
} }
if *requireVersion != "" && *requireVersion != version { if *requireVersion != "" && *requireVersion != version {
fmt.Println("Please restart/reload this terminal session " + out.FatalVersionMismatch(version, *requireVersion)
"(resh version: " + version +
"; resh version of this terminal session: " + *requireVersion +
")")
os.Exit(3)
} }
if *requireRevision != "" && *requireRevision != commit { if *requireRevision != "" && *requireRevision != commit {
fmt.Println("Please restart/reload this terminal session " + // this is only relevant for dev versions so we can reuse FatalVersionMismatch()
"(resh revision: " + commit + out.FatalVersionMismatch("revision "+commit, "revision "+*requireVersion)
"; resh revision of this terminal session: " + *requireRevision +
")")
os.Exit(3)
}
realtimeAfter, err := strconv.ParseFloat(*rta, 64)
if err != nil {
out.Fatal("Error while parsing flag --realtimeAfter", err)
} }
realtimeBefore, err := strconv.ParseFloat(*rtb, 64)
if err != nil {
out.Fatal("Error while parsing flag --realtimeBefore", err)
}
realtimeDuration := realtimeAfter - realtimeBefore
timezoneAfterOffset := collect.GetTimezoneOffsetInSeconds(logger, *timezoneAfter)
realtimeAfterLocal := realtimeAfter + timezoneAfterOffset
realPwdAfter, err := filepath.EvalSymlinks(*pwdAfter) timeAfter, err := strconv.ParseFloat(*rta, 64)
if err != nil { if err != nil {
logger.Error("Error while handling pwdAfter realpath", zap.Error(err)) out.Fatal("Error while parsing flag --timeAfter", err)
realPwdAfter = ""
} }
timeBefore, err := strconv.ParseFloat(*rtb, 64)
gitDirAfter, gitRealDirAfter := collect.GetGitDirs(logger, *gitCdupAfter, *gitCdupExitCodeAfter, *pwdAfter) if err != nil {
if *gitRemoteExitCodeAfter != 0 { out.Fatal("Error while parsing flag --timeBefore", err)
*gitRemoteAfter = ""
} }
duration := timeAfter - timeBefore
rec := records.Record{ // FIXME: use recordint.Postollect
// core rec := recordint.Collect{
BaseRecord: records.BaseRecord{
CmdLine: *cmdLine,
ExitCode: *exitCode,
SessionID: *sessionID, SessionID: *sessionID,
RecordID: *recordID,
Shlvl: *shlvl, Shlvl: *shlvl,
Shell: *shell,
PwdAfter: *pwdAfter,
// non-posix
RealPwdAfter: realPwdAfter,
// before after Rec: record.V1{
TimezoneAfter: *timezoneAfter, RecordID: *recordID,
SessionID: *sessionID,
RealtimeBefore: realtimeBefore,
RealtimeAfter: realtimeAfter,
RealtimeAfterLocal: realtimeAfterLocal,
RealtimeDuration: realtimeDuration,
GitDirAfter: gitDirAfter,
GitRealDirAfter: gitRealDirAfter,
GitOriginRemoteAfter: *gitRemoteAfter,
MachineID: collect.ReadFileContent(out.Logger, machineIDPath),
PartOne: false, ExitCode: *exitCode,
Duration: duration,
ReshUUID: collect.ReadFileContent(out.Logger, reshUUIDPath), PartsNotMerged: true,
ReshVersion: version,
ReshRevision: commit,
}, },
} }
collect.SendRecord(out, rec, strconv.Itoa(config.Port), "/record") collect.SendRecord(out, rec, strconv.Itoa(config.Port), "/record")

@ -9,10 +9,9 @@ import (
"github.com/curusarn/resh/internal/collect" "github.com/curusarn/resh/internal/collect"
"github.com/curusarn/resh/internal/logger" "github.com/curusarn/resh/internal/logger"
"github.com/curusarn/resh/internal/output" "github.com/curusarn/resh/internal/output"
"github.com/curusarn/resh/internal/records" "github.com/curusarn/resh/internal/recordint"
"go.uber.org/zap" "go.uber.org/zap"
"path/filepath"
"strconv" "strconv"
) )
@ -30,61 +29,15 @@ func main() {
} }
out := output.New(logger, "resh-collect ERROR") out := output.New(logger, "resh-collect ERROR")
homeDir, err := os.UserHomeDir()
if err != nil {
out.Fatal("Could not get user home dir", err)
}
reshUUIDPath := filepath.Join(homeDir, "/.resh/resh-uuid")
machineIDPath := "/etc/machine-id"
showVersion := flag.Bool("version", false, "Show version and exit") showVersion := flag.Bool("version", false, "Show version and exit")
showRevision := flag.Bool("revision", false, "Show git revision and exit") showRevision := flag.Bool("revision", false, "Show git revision and exit")
requireVersion := flag.String("requireVersion", "", "abort if version doesn't match") requireVersion := flag.String("requireVersion", "", "abort if version doesn't match")
requireRevision := flag.String("requireRevision", "", "abort if revision doesn't match") requireRevision := flag.String("requireRevision", "", "abort if revision doesn't match")
shell := flag.String("shell", "", "actual shell")
uname := flag.String("uname", "", "uname")
sessionID := flag.String("sessionId", "", "resh generated session id") sessionID := flag.String("sessionId", "", "resh generated session id")
// posix variables sessionPID := flag.Int("sessionPid", -1, "$$ at session start")
cols := flag.String("cols", "-1", "$COLUMNS")
lines := flag.String("lines", "-1", "$LINES")
home := flag.String("home", "", "$HOME")
lang := flag.String("lang", "", "$LANG")
lcAll := flag.String("lcAll", "", "$LC_ALL")
login := flag.String("login", "", "$LOGIN")
shellEnv := flag.String("shellEnv", "", "$SHELL")
term := flag.String("term", "", "$TERM")
// non-posix
pid := flag.Int("pid", -1, "$$")
sessionPid := flag.Int("sessionPid", -1, "$$ at session start")
shlvl := flag.Int("shlvl", -1, "$SHLVL")
host := flag.String("host", "", "$HOSTNAME")
hosttype := flag.String("hosttype", "", "$HOSTTYPE")
ostype := flag.String("ostype", "", "$OSTYPE")
machtype := flag.String("machtype", "", "$MACHTYPE")
// before after
timezoneBefore := flag.String("timezoneBefore", "", "")
osReleaseID := flag.String("osReleaseId", "", "/etc/os-release ID")
osReleaseVersionID := flag.String("osReleaseVersionId", "",
"/etc/os-release ID")
osReleaseIDLike := flag.String("osReleaseIdLike", "", "/etc/os-release ID")
osReleaseName := flag.String("osReleaseName", "", "/etc/os-release ID")
osReleasePrettyName := flag.String("osReleasePrettyName", "",
"/etc/os-release ID")
rtb := flag.String("realtimeBefore", "-1", "before $EPOCHREALTIME")
rtsess := flag.String("realtimeSession", "-1",
"on session start $EPOCHREALTIME")
rtsessboot := flag.String("realtimeSessSinceBoot", "-1",
"on session start $EPOCHREALTIME")
flag.Parse() flag.Parse()
if *showVersion == true { if *showVersion == true {
@ -96,96 +49,16 @@ func main() {
os.Exit(0) os.Exit(0)
} }
if *requireVersion != "" && *requireVersion != version { if *requireVersion != "" && *requireVersion != version {
fmt.Println("Please restart/reload this terminal session " + out.FatalVersionMismatch(version, *requireVersion)
"(resh version: " + version +
"; resh version of this terminal session: " + *requireVersion +
")")
os.Exit(3)
} }
if *requireRevision != "" && *requireRevision != commit { if *requireRevision != "" && *requireRevision != commit {
fmt.Println("Please restart/reload this terminal session " + // this is only relevant for dev versions so we can reuse FatalVersionMismatch()
"(resh revision: " + commit + out.FatalVersionMismatch("revision "+commit, "revision "+*requireVersion)
"; resh revision of this terminal session: " + *requireRevision +
")")
os.Exit(3)
}
realtimeBefore, err := strconv.ParseFloat(*rtb, 64)
if err != nil {
out.Fatal("Error while parsing flag --realtimeBefore", err)
} }
realtimeSessionStart, err := strconv.ParseFloat(*rtsess, 64)
if err != nil {
out.Fatal("Error while parsing flag --realtimeSession", err)
}
realtimeSessSinceBoot, err := strconv.ParseFloat(*rtsessboot, 64)
if err != nil {
out.Fatal("Error while parsing flag --realtimeSessSinceBoot", err)
}
realtimeSinceSessionStart := realtimeBefore - realtimeSessionStart
realtimeSinceBoot := realtimeSessSinceBoot + realtimeSinceSessionStart
timezoneBeforeOffset := collect.GetTimezoneOffsetInSeconds(logger, *timezoneBefore)
realtimeBeforeLocal := realtimeBefore + timezoneBeforeOffset
if *osReleaseID == "" { rec := recordint.SessionInit{
*osReleaseID = "linux"
}
if *osReleaseName == "" {
*osReleaseName = "Linux"
}
if *osReleasePrettyName == "" {
*osReleasePrettyName = "Linux"
}
rec := records.Record{
// posix
Cols: *cols,
Lines: *lines,
// core
BaseRecord: records.BaseRecord{
Shell: *shell,
Uname: *uname,
SessionID: *sessionID, SessionID: *sessionID,
SessionPID: *sessionPID,
// posix
Home: *home,
Lang: *lang,
LcAll: *lcAll,
Login: *login,
// Path: *path,
ShellEnv: *shellEnv,
Term: *term,
// non-posix
Pid: *pid,
SessionPID: *sessionPid,
Host: *host,
Hosttype: *hosttype,
Ostype: *ostype,
Machtype: *machtype,
Shlvl: *shlvl,
// before after
TimezoneBefore: *timezoneBefore,
RealtimeBefore: realtimeBefore,
RealtimeBeforeLocal: realtimeBeforeLocal,
RealtimeSinceSessionStart: realtimeSinceSessionStart,
RealtimeSinceBoot: realtimeSinceBoot,
MachineID: collect.ReadFileContent(out.Logger, machineIDPath),
OsReleaseID: *osReleaseID,
OsReleaseVersionID: *osReleaseVersionID,
OsReleaseIDLike: *osReleaseIDLike,
OsReleaseName: *osReleaseName,
OsReleasePrettyName: *osReleasePrettyName,
ReshUUID: collect.ReadFileContent(out.Logger, reshUUIDPath),
ReshVersion: version,
ReshRevision: commit,
},
} }
collect.SendRecord(out, rec, strconv.Itoa(config.Port), "/session_init") collect.SendSessionInit(out, rec, strconv.Itoa(config.Port))
} }

@ -5,7 +5,6 @@ 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/mattn/go-shellwords v1.0.12
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

@ -193,8 +193,6 @@ github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=

@ -92,6 +92,7 @@ func getConfig() (*configFile, error) {
return readConfig(path) return readConfig(path)
} }
// returned config is always usable, returned errors are informative
func processAndFillDefaults(configF *configFile) (Config, error) { func processAndFillDefaults(configF *configFile) (Config, error) {
config := defaults config := defaults

@ -11,14 +11,14 @@ import (
"time" "time"
"github.com/curusarn/resh/internal/output" "github.com/curusarn/resh/internal/output"
"github.com/curusarn/resh/internal/records" "github.com/curusarn/resh/internal/recordint"
"go.uber.org/zap" "go.uber.org/zap"
) )
// SendRecord to daemon // SendRecord to daemon
func SendRecord(out *output.Output, r records.Record, port, path string) { func SendRecord(out *output.Output, r recordint.Collect, port, path string) {
out.Logger.Debug("Sending record ...", out.Logger.Debug("Sending record ...",
zap.String("cmdLine", r.CmdLine), zap.String("cmdLine", r.Rec.CmdLine),
zap.String("sessionID", r.SessionID), zap.String("sessionID", r.SessionID),
) )
recJSON, err := json.Marshal(r) recJSON, err := json.Marshal(r)
@ -42,6 +42,33 @@ func SendRecord(out *output.Output, r records.Record, port, path string) {
} }
} }
// SendSessionInit to daemon
func SendSessionInit(out *output.Output, r recordint.SessionInit, port string) {
out.Logger.Debug("Sending session init ...",
zap.String("sessionID", r.SessionID),
zap.Int("sessionPID", r.SessionPID),
)
recJSON, err := json.Marshal(r)
if err != nil {
out.Fatal("Error while encoding record", err)
}
req, err := http.NewRequest("POST", "http://localhost:"+port+"/session_init",
bytes.NewBuffer(recJSON))
if err != nil {
out.Fatal("Error while sending record", err)
}
req.Header.Set("Content-Type", "application/json")
client := http.Client{
Timeout: 1 * time.Second,
}
_, err = client.Do(req)
if err != nil {
out.FatalDaemonNotRunning(err)
}
}
// ReadFileContent and return it as a string // ReadFileContent and return it as a string
func ReadFileContent(logger *zap.Logger, path string) string { func ReadFileContent(logger *zap.Logger, path string) string {
dat, err := ioutil.ReadFile(path) dat, err := ioutil.ReadFile(path)

@ -1,13 +1,13 @@
package histcli package histcli
import ( import (
"github.com/curusarn/resh/internal/records" "github.com/curusarn/resh/internal/recordint"
) )
// Histcli is a dump of history preprocessed for resh cli purposes // Histcli is a dump of history preprocessed for resh cli purposes
type Histcli struct { type Histcli struct {
// list of records // list of records
List []records.CliRecord List []recordint.SearchApp
} }
// New Histcli // New Histcli
@ -16,16 +16,15 @@ func New() Histcli {
} }
// AddRecord to the histcli // AddRecord to the histcli
func (h *Histcli) AddRecord(record records.Record) { func (h *Histcli) AddRecord(rec *recordint.Indexed) {
enriched := records.Enriched(record) cli := recordint.NewSearchApp(rec)
cli := records.NewCliRecord(enriched)
h.List = append(h.List, cli) h.List = append(h.List, cli)
} }
// AddCmdLine to the histcli // AddCmdLine to the histcli
func (h *Histcli) AddCmdLine(cmdline string) { func (h *Histcli) AddCmdLine(cmdline string) {
cli := records.NewCliRecordFromCmdLine(cmdline) cli := recordint.NewSearchAppFromCmdLine(cmdline)
h.List = append(h.List, cli) h.List = append(h.List, cli)
} }

@ -1,7 +1,6 @@
package histfile package histfile
import ( import (
"encoding/json"
"math" "math"
"os" "os"
"strconv" "strconv"
@ -9,42 +8,48 @@ import (
"github.com/curusarn/resh/internal/histcli" "github.com/curusarn/resh/internal/histcli"
"github.com/curusarn/resh/internal/histlist" "github.com/curusarn/resh/internal/histlist"
"github.com/curusarn/resh/internal/recio"
"github.com/curusarn/resh/internal/record"
"github.com/curusarn/resh/internal/recordint"
"github.com/curusarn/resh/internal/records" "github.com/curusarn/resh/internal/records"
"github.com/curusarn/resh/internal/recutil"
"go.uber.org/zap" "go.uber.org/zap"
) )
// TODO: get rid of histfile - use histio instead
// Histfile writes records to histfile // Histfile writes records to histfile
type Histfile struct { type Histfile struct {
sugar *zap.SugaredLogger sugar *zap.SugaredLogger
sessionsMutex sync.Mutex sessionsMutex sync.Mutex
sessions map[string]records.Record sessions map[string]recordint.Collect
historyPath string historyPath string
recentMutex sync.Mutex
recentRecords []records.Record
// NOTE: we have separate histories which only differ if there was not enough resh_history // NOTE: we have separate histories which only differ if there was not enough resh_history
// resh_history itself is common for both bash and zsh // resh_history itself is common for both bash and zsh
bashCmdLines histlist.Histlist bashCmdLines histlist.Histlist
zshCmdLines histlist.Histlist zshCmdLines histlist.Histlist
cliRecords histcli.Histcli cliRecords histcli.Histcli
rio *recio.RecIO
} }
// New creates new histfile and runs its gorutines // New creates new histfile and runs its gorutines
func New(sugar *zap.SugaredLogger, input chan records.Record, sessionsToDrop chan string, func New(sugar *zap.SugaredLogger, input chan recordint.Collect, sessionsToDrop chan string,
reshHistoryPath string, bashHistoryPath string, zshHistoryPath string, reshHistoryPath string, bashHistoryPath string, zshHistoryPath string,
maxInitHistSize int, minInitHistSizeKB int, maxInitHistSize int, minInitHistSizeKB int,
signals chan os.Signal, shutdownDone chan string) *Histfile { signals chan os.Signal, shutdownDone chan string) *Histfile {
rio := recio.New(sugar.With("module", "histfile"))
hf := Histfile{ hf := Histfile{
sugar: sugar.With("module", "histfile"), sugar: sugar.With("module", "histfile"),
sessions: map[string]records.Record{}, sessions: map[string]recordint.Collect{},
historyPath: reshHistoryPath, historyPath: reshHistoryPath,
bashCmdLines: histlist.New(sugar), bashCmdLines: histlist.New(sugar),
zshCmdLines: histlist.New(sugar), zshCmdLines: histlist.New(sugar),
cliRecords: histcli.New(), cliRecords: histcli.New(),
rio: &rio,
} }
go hf.loadHistory(bashHistoryPath, zshHistoryPath, maxInitHistSize, minInitHistSizeKB) go hf.loadHistory(bashHistoryPath, zshHistoryPath, maxInitHistSize, minInitHistSizeKB)
go hf.writer(input, signals, shutdownDone) go hf.writer(input, signals, shutdownDone)
@ -53,7 +58,7 @@ func New(sugar *zap.SugaredLogger, input chan records.Record, sessionsToDrop cha
} }
// load records from resh history, reverse, enrich and save // load records from resh history, reverse, enrich and save
func (h *Histfile) loadCliRecords(recs []records.Record) { func (h *Histfile) loadCliRecords(recs []recordint.Indexed) {
for _, cmdline := range h.bashCmdLines.List { for _, cmdline := range h.bashCmdLines.List {
h.cliRecords.AddCmdLine(cmdline) h.cliRecords.AddCmdLine(cmdline)
} }
@ -62,7 +67,7 @@ func (h *Histfile) loadCliRecords(recs []records.Record) {
} }
for i := len(recs) - 1; i >= 0; i-- { for i := len(recs) - 1; i >= 0; i-- {
rec := recs[i] rec := recs[i]
h.cliRecords.AddRecord(rec) h.cliRecords.AddRecord(&rec)
} }
h.sugar.Infow("Resh history loaded", h.sugar.Infow("Resh history loaded",
"historyRecordsCount", len(h.cliRecords.List), "historyRecordsCount", len(h.cliRecords.List),
@ -71,8 +76,6 @@ func (h *Histfile) loadCliRecords(recs []records.Record) {
// loadsHistory from resh_history and if there is not enough of it also load native shell histories // loadsHistory from resh_history and if there is not enough of it also load native shell histories
func (h *Histfile) loadHistory(bashHistoryPath, zshHistoryPath string, maxInitHistSize, minInitHistSizeKB int) { func (h *Histfile) loadHistory(bashHistoryPath, zshHistoryPath string, maxInitHistSize, minInitHistSizeKB int) {
h.recentMutex.Lock()
defer h.recentMutex.Unlock()
h.sugar.Infow("Checking if resh_history is large enough ...") h.sugar.Infow("Checking if resh_history is large enough ...")
fi, err := os.Stat(h.historyPath) fi, err := os.Stat(h.historyPath)
var size int var size int
@ -95,7 +98,10 @@ func (h *Histfile) loadHistory(bashHistoryPath, zshHistoryPath string, maxInitHi
h.sugar.Debugw("Loading resh history from file ...", h.sugar.Debugw("Loading resh history from file ...",
"historyFile", h.historyPath, "historyFile", h.historyPath,
) )
history := records.LoadFromFile(h.sugar, h.historyPath) history, err := h.rio.ReadAndFixFile(h.historyPath, 3)
if err != nil {
h.sugar.Panicf("Failed to read file: %w", err)
}
h.sugar.Infow("Resh history loaded from file", h.sugar.Infow("Resh history loaded from file",
"historyFile", h.historyPath, "historyFile", h.historyPath,
"recordCount", len(history), "recordCount", len(history),
@ -130,7 +136,7 @@ func (h *Histfile) sessionGC(sessionsToDrop chan string) {
if part1, found := h.sessions[session]; found == true { if part1, found := h.sessions[session]; found == true {
sugar.Infow("Dropping session") sugar.Infow("Dropping session")
delete(h.sessions, session) delete(h.sessions, session)
go writeRecord(sugar, part1, h.historyPath) go h.rio.AppendToFile(h.historyPath, []record.V1{part1.Rec})
} else { } else {
sugar.Infow("No hanging parts for session - nothing to drop") sugar.Infow("No hanging parts for session - nothing to drop")
} }
@ -139,43 +145,43 @@ func (h *Histfile) sessionGC(sessionsToDrop chan string) {
} }
// writer reads records from channel, merges them and writes them to file // writer reads records from channel, merges them and writes them to file
func (h *Histfile) writer(input chan records.Record, signals chan os.Signal, shutdownDone chan string) { func (h *Histfile) writer(collect chan recordint.Collect, signals chan os.Signal, shutdownDone chan string) {
for { for {
func() { func() {
select { select {
case record := <-input: case rec := <-collect:
part := "2" part := "2"
if record.PartOne { if rec.Rec.PartOne {
part = "1" part = "1"
} }
sugar := h.sugar.With( sugar := h.sugar.With(
"recordCmdLine", record.CmdLine, "recordCmdLine", rec.Rec.CmdLine,
"recordPart", part, "recordPart", part,
"recordShell", record.Shell, "recordShell", rec.Shell,
) )
sugar.Debugw("Got record") sugar.Debugw("Got record")
h.sessionsMutex.Lock() h.sessionsMutex.Lock()
defer h.sessionsMutex.Unlock() defer h.sessionsMutex.Unlock()
// allows nested sessions to merge records properly // allows nested sessions to merge records properly
mergeID := record.SessionID + "_" + strconv.Itoa(record.Shlvl) mergeID := rec.SessionID + "_" + strconv.Itoa(rec.Shlvl)
sugar = sugar.With("mergeID", mergeID) sugar = sugar.With("mergeID", mergeID)
if record.PartOne { if rec.Rec.PartOne {
if _, found := h.sessions[mergeID]; found { if _, found := h.sessions[mergeID]; found {
msg := "Got another first part of the records before merging the previous one - overwriting!" msg := "Got another first part of the records before merging the previous one - overwriting!"
if record.Shell == "zsh" { if rec.Shell == "zsh" {
sugar.Warnw(msg) sugar.Warnw(msg)
} else { } else {
sugar.Infow(msg + " Unfortunately this is normal in bash, it can't be prevented.") sugar.Infow(msg + " Unfortunately this is normal in bash, it can't be prevented.")
} }
} }
h.sessions[mergeID] = record h.sessions[mergeID] = rec
} else { } else {
if part1, found := h.sessions[mergeID]; found == false { if part1, found := h.sessions[mergeID]; found == false {
sugar.Warnw("Got second part of record and nothing to merge it with - ignoring!") sugar.Warnw("Got second part of record and nothing to merge it with - ignoring!")
} else { } else {
delete(h.sessions, mergeID) delete(h.sessions, mergeID)
go h.mergeAndWriteRecord(sugar, part1, record) go h.mergeAndWriteRecord(sugar, part1, rec)
} }
} }
case sig := <-signals: case sig := <-signals:
@ -187,11 +193,11 @@ func (h *Histfile) writer(input chan records.Record, signals chan os.Signal, shu
defer h.sessionsMutex.Unlock() defer h.sessionsMutex.Unlock()
sugar.Debugw("Unlocked mutex") sugar.Debugw("Unlocked mutex")
for sessID, record := range h.sessions { for sessID, rec := range h.sessions {
sugar.Warnw("Writing incomplete record for session", sugar.Warnw("Writing incomplete record for session",
"sessionID", sessID, "sessionID", sessID,
) )
h.writeRecord(sugar, record) h.writeRecord(sugar, rec.Rec)
} }
sugar.Debugw("Shutdown successful") sugar.Debugw("Shutdown successful")
shutdownDone <- "histfile" shutdownDone <- "histfile"
@ -201,52 +207,53 @@ func (h *Histfile) writer(input chan records.Record, signals chan os.Signal, shu
} }
} }
func (h *Histfile) writeRecord(sugar *zap.SugaredLogger, part1 records.Record) { func (h *Histfile) writeRecord(sugar *zap.SugaredLogger, rec record.V1) {
writeRecord(sugar, part1, h.historyPath) h.rio.AppendToFile(h.historyPath, []record.V1{rec})
} }
func (h *Histfile) mergeAndWriteRecord(sugar *zap.SugaredLogger, part1, part2 records.Record) { func (h *Histfile) mergeAndWriteRecord(sugar *zap.SugaredLogger, part1 recordint.Collect, part2 recordint.Collect) {
err := part1.Merge(part2) rec, err := recutil.Merge(&part1, &part2)
if err != nil { if err != nil {
sugar.Errorw("Error while merging records", "error", err) sugar.Errorw("Error while merging records", "error", err)
return return
} }
func() { func() {
h.recentMutex.Lock() cmdLine := rec.CmdLine
defer h.recentMutex.Unlock()
h.recentRecords = append(h.recentRecords, part1)
cmdLine := part1.CmdLine
h.bashCmdLines.AddCmdLine(cmdLine) h.bashCmdLines.AddCmdLine(cmdLine)
h.zshCmdLines.AddCmdLine(cmdLine) h.zshCmdLines.AddCmdLine(cmdLine)
h.cliRecords.AddRecord(part1) h.cliRecords.AddRecord(&recordint.Indexed{
// TODO: is this what we want?
Rec: rec,
})
}() }()
writeRecord(sugar, part1, h.historyPath) h.rio.AppendToFile(h.historyPath, []record.V1{rec})
} }
func writeRecord(sugar *zap.SugaredLogger, rec records.Record, outputPath string) { // TODO: use errors in RecIO
recJSON, err := json.Marshal(rec) // func writeRecord(sugar *zap.SugaredLogger, rec record.V1, outputPath string) {
if err != nil { // recJSON, err := json.Marshal(rec)
sugar.Errorw("Marshalling error", "error", err) // if err != nil {
return // sugar.Errorw("Marshalling error", "error", err)
} // return
f, err := os.OpenFile(outputPath, // }
os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) // f, err := os.OpenFile(outputPath,
if err != nil { // os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
sugar.Errorw("Could not open file", "error", err) // if err != nil {
return // sugar.Errorw("Could not open file", "error", err)
} // return
defer f.Close() // }
_, err = f.Write(append(recJSON, []byte("\n")...)) // defer f.Close()
if err != nil { // _, err = f.Write(append(recJSON, []byte("\n")...))
sugar.Errorw("Error while writing record", // if err != nil {
"recordRaw", rec, // sugar.Errorw("Error while writing record",
"error", err, // "recordRaw", rec,
) // "error", err,
return // )
} // return
} // }
// }
// DumpCliRecords returns enriched records // DumpCliRecords returns enriched records
func (h *Histfile) DumpCliRecords() histcli.Histcli { func (h *Histfile) DumpCliRecords() histcli.Histcli {
@ -254,13 +261,13 @@ func (h *Histfile) DumpCliRecords() histcli.Histcli {
return h.cliRecords return h.cliRecords
} }
func loadCmdLines(sugar *zap.SugaredLogger, recs []records.Record) histlist.Histlist { func loadCmdLines(sugar *zap.SugaredLogger, recs []recordint.Indexed) histlist.Histlist {
hl := histlist.New(sugar) hl := histlist.New(sugar)
// go from bottom and deduplicate // go from bottom and deduplicate
var cmdLines []string var cmdLines []string
cmdLinesSet := map[string]bool{} cmdLinesSet := map[string]bool{}
for i := len(recs) - 1; i >= 0; i-- { for i := len(recs) - 1; i >= 0; i-- {
cmdLine := recs[i].CmdLine cmdLine := recs[i].Rec.CmdLine
if cmdLinesSet[cmdLine] { if cmdLinesSet[cmdLine] {
continue continue
} }

@ -1,5 +1,7 @@
package msg package msg
import "github.com/curusarn/resh/internal/recordint"
// CliMsg struct // CliMsg struct
type CliMsg struct { type CliMsg struct {
SessionID string SessionID string
@ -8,7 +10,7 @@ type CliMsg struct {
// CliResponse struct // CliResponse struct
type CliResponse struct { type CliResponse struct {
Records []record.SearchApp Records []recordint.SearchApp
} }
// StatusResponse struct // StatusResponse struct

@ -39,7 +39,7 @@ func (f *Output) Fatal(msg string, err error) {
var msgDeamonNotRunning = `Resh-daemon didn't respond - it's probably not running. var msgDeamonNotRunning = `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: ~/.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)
-> You can create an issue at: https://github.com/curusarn/resh/issues -> You can create an issue at: https://github.com/curusarn/resh/issues
` `
var msgVersionMismatch = `This terminal session was started with different resh version than is installed now. var msgVersionMismatch = `This terminal session was started with different resh version than is installed now.
@ -58,12 +58,18 @@ func (f *Output) FatalDaemonNotRunning(err error) {
f.Logger.Fatal("Daemon is not running", zap.Error(err)) f.Logger.Fatal("Daemon is not running", zap.Error(err))
} }
func (f *Output) ErrorVersionMismatch(err error) { func (f *Output) ErrorVersionMismatch(installedVer, terminalVer string) {
fmt.Fprintf(os.Stderr, "%s: %s", f.ErrPrefix, msgVersionMismatch) fmt.Fprintf(os.Stderr, "%s: %s\n\n(installed version: %s, this terminal version: %s)",
f.Logger.Fatal("Version mismatch", zap.Error(err)) f.ErrPrefix, msgVersionMismatch, installedVer, terminalVer)
f.Logger.Fatal("Version mismatch",
zap.String("installed", installedVer),
zap.String("terminal", terminalVer))
} }
func (f *Output) FatalVersionMismatch(err error) { func (f *Output) FatalVersionMismatch(installedVer, terminalVer string) {
fmt.Fprintf(os.Stderr, "%s: %s", f.ErrPrefix, msgVersionMismatch) fmt.Fprintf(os.Stderr, "%s: %s\n\n(installed version: %s, this terminal version: %s)",
f.Logger.Fatal("Version mismatch", zap.Error(err)) f.ErrPrefix, msgVersionMismatch, installedVer, terminalVer)
f.Logger.Fatal("Version mismatch",
zap.String("installed", installedVer),
zap.String("terminal", terminalVer))
} }

@ -1,9 +1,35 @@
package recconv package recconv
import "github.com/curusarn/resh/internal/record" import (
"github.com/curusarn/resh/internal/record"
)
func LegacyToV1(r *record.Legacy) *record.V1 { func LegacyToV1(r *record.Legacy) *record.V1 {
return &record.V1{ return &record.V1{
// FIXME: fill in all the fields // FIXME: fill in all the fields
// Flags: 0,
DeviceID: r.MachineID,
SessionID: r.SessionID,
RecordID: r.RecordID,
CmdLine: r.CmdLine,
ExitCode: r.ExitCode,
Home: r.Home,
Pwd: r.Pwd,
RealPwd: r.RealPwd,
Logname: r.Login,
Hostname: r.Host,
GitOriginRemote: r.GitOriginRemote,
Time: r.RealtimeBefore,
Duration: r.RealtimeDuration,
PartOne: r.PartOne,
PartsNotMerged: !r.PartsMerged,
} }
} }

@ -48,7 +48,7 @@ func (r *RecIO) ReadAndFixFile(fpath string, maxErrors int) ([]recordint.Indexed
for _, rec := range recs { for _, rec := range recs {
recsV1 = append(recsV1, rec.Rec) recsV1 = append(recsV1, rec.Rec)
} }
err = r.WriteFile(fpath, recsV1) err = r.OverwriteFile(fpath, recsV1)
if err != nil { if err != nil {
r.sugar.Errorw("Failed write fixed history file - aborting fixing history file", r.sugar.Errorw("Failed write fixed history file - aborting fixing history file",
"filename", fpath, "filename", fpath,

@ -10,37 +10,53 @@ import (
) )
// TODO: better errors // TODO: better errors
func (r *RecIO) WriteFile(fpath string, data []record.V1) error { func (r *RecIO) OverwriteFile(fpath string, recs []record.V1) error {
file, err := os.Create(fpath) file, err := os.Create(fpath)
if err != nil { if err != nil {
return err return err
} }
defer file.Close() defer file.Close()
for _, rec := range data { return writeRecords(file, recs)
jsn, err := encodeV1Record(rec)
if err != nil {
return err
} }
_, err = file.Write(jsn)
// TODO: better errors
func (r *RecIO) AppendToFile(fpath string, recs []record.V1) error {
file, err := os.OpenFile(fpath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil { if err != nil {
return err return err
} }
} defer file.Close()
return nil return writeRecords(file, recs)
} }
// TODO: better errors
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"
// scan to the correct line // scan to the correct line
r.sugar.Error("not implemented yet (FIXME)")
return nil
}
func writeRecords(file *os.File, recs []record.V1) error {
for _, rec := range recs {
jsn, err := encodeV1Record(rec)
if err != nil {
return err
}
_, err = file.Write(jsn)
if err != nil {
return err
}
}
return nil return nil
} }
func encodeV1Record(rec record.V1) ([]byte, error) { func encodeV1Record(rec record.V1) ([]byte, error) {
version := []byte("v1")
jsn, err := json.Marshal(rec) jsn, err := json.Marshal(rec)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to encode json: %w", err) return nil, fmt.Errorf("failed to encode json: %w", err)
} }
return append(jsn, []byte("\n")...), nil return append(append(version, jsn...), []byte("\n")...), nil
} }

@ -16,6 +16,7 @@ type V1 struct {
ExitCode int `json:"exitCode"` ExitCode int `json:"exitCode"`
// paths // paths
// TODO: Do we need both pwd and real pwd?
Home string `json:"home"` Home string `json:"home"`
Pwd string `json:"pwd"` Pwd string `json:"pwd"`
RealPwd string `json:"realPwd"` RealPwd string `json:"realPwd"`
@ -27,8 +28,9 @@ type V1 struct {
// git info // git info
// origin is the most important // origin is the most important
GitOriginRemote string `json:"gitOriginRemote"` GitOriginRemote string `json:"gitOriginRemote"`
// TODO: add GitBranch (v2 ?)
// maybe branch could be useful - e.g. in monorepo ?? // maybe branch could be useful - e.g. in monorepo ??
GitBranch string `json:"gitBranch"` // GitBranch string `json:"gitBranch"`
// what is this for ?? // what is this for ??
// session watching needs this // session watching needs this
@ -36,11 +38,11 @@ type V1 struct {
// records belong to sessions // records belong to sessions
// PID int `json:"pid"` // PID int `json:"pid"`
// needed for tracking of sessions but I think it shouldn't be part of V1 // needed for tracking of sessions but I think it shouldn't be part of V1
SessionPID int `json:"sessionPID"` // SessionPID int `json:"sessionPID"`
// needed to because records are merged with parts with same "SessionID + Shlvl" // needed to because records are merged with parts with same "SessionID + Shlvl"
// I don't think we need to save it // I don't think we need to save it
Shlvl int `json:"shlvl"` // Shlvl int `json:"shlvl"`
// time (before), duration of command // time (before), duration of command
Time float64 `json:"time"` Time float64 `json:"time"`

@ -8,6 +8,7 @@ type Collect struct {
Shlvl int Shlvl int
// session watching // session watching
SessionPID int SessionPID int
Shell string
Rec record.V1 Rec record.V1
} }
@ -18,4 +19,16 @@ type Postcollect struct {
Shlvl int Shlvl int
// session watching // session watching
SessionPID int SessionPID int
RecordID string
ExitCode int
Duration float64
}
type SessionInit struct {
// record merging
SessionID string
Shlvl int
// session watching
SessionPID int
} }

@ -1,51 +0,0 @@
package recordint
import (
"github.com/curusarn/resh/internal/record"
"github.com/curusarn/resh/internal/recutil"
)
// TODO: This all seems excessive
// TODO: V1 should be converted directly to SearchApp record
// EnrichedRecord - record enriched with additional data
type Enriched struct {
// TODO: think about if it really makes sense to have this based on V1
record.V1
// TODO: drop some/all of this
// enriching fields - added "later"
Command string `json:"command"`
FirstWord string `json:"firstWord"`
Invalid bool `json:"invalid"`
SeqSessionID uint64 `json:"seqSessionId"`
LastRecordOfSession bool `json:"lastRecordOfSession"`
DebugThisRecord bool `json:"debugThisRecord"`
Errors []string `json:"errors"`
// SeqSessionID uint64 `json:"seqSessionId,omitempty"`
}
// Enriched - returns enriched record
func NewEnrichedFromV1(r *record.V1) Enriched {
rec := Enriched{Record: r}
// normlize git remote
rec.GitOriginRemote = NormalizeGitRemote(rec.GitOriginRemote)
rec.GitOriginRemoteAfter = NormalizeGitRemote(rec.GitOriginRemoteAfter)
// Get command/first word from commandline
var err error
err = recutil.Validate(r)
if err != nil {
rec.Errors = append(rec.Errors, "Validate error:"+err.Error())
// rec, _ := record.ToString()
// sugar.Println("Invalid command:", rec)
rec.Invalid = true
}
rec.Command, rec.FirstWord, err = GetCommandAndFirstWord(r.CmdLine)
if err != nil {
rec.Errors = append(rec.Errors, "GetCommandAndFirstWord error:"+err.Error())
// rec, _ := record.ToString()
// sugar.Println("Invalid command:", rec)
rec.Invalid = true // should this be really invalid ?
}
return rec
}

@ -1,5 +1,12 @@
package recordint package recordint
import (
"net/url"
"strings"
giturls "github.com/whilp/git-urls"
)
// SearchApp record used for sending records to RESH-CLI // SearchApp record used for sending records to RESH-CLI
type SearchApp struct { type SearchApp struct {
IsRaw bool IsRaw bool
@ -14,6 +21,9 @@ type SearchApp struct {
ExitCode int ExitCode int
Time float64 Time float64
// file index
Idx int
} }
// NewCliRecordFromCmdLine // NewCliRecordFromCmdLine
@ -25,16 +35,39 @@ func NewSearchAppFromCmdLine(cmdLine string) SearchApp {
} }
// NewCliRecord from EnrichedRecord // NewCliRecord from EnrichedRecord
func NewSearchApp(r *Enriched) SearchApp { func NewSearchApp(r *Indexed) SearchApp {
// TODO: we used to validate records with recutil.Validate()
return SearchApp{ return SearchApp{
IsRaw: false, IsRaw: false,
SessionID: r.SessionID, SessionID: r.Rec.SessionID,
CmdLine: r.CmdLine, CmdLine: r.Rec.CmdLine,
Host: r.Hostname, Host: r.Rec.Hostname,
Pwd: r.Pwd, Pwd: r.Rec.Pwd,
Home: r.Home, Home: r.Rec.Home,
GitOriginRemote: r.GitOriginRemote, // TODO: is this the right place to normalize the git remote
ExitCode: r.ExitCode, GitOriginRemote: normalizeGitRemote(r.Rec.GitOriginRemote),
Time: r.Time, ExitCode: r.Rec.ExitCode,
Time: r.Rec.Time,
Idx: r.Idx,
}
}
// TODO: maybe move this to a more appropriate place
// normalizeGitRemote helper
func normalizeGitRemote(gitRemote string) string {
if strings.HasSuffix(gitRemote, ".git") {
gitRemote = gitRemote[:len(gitRemote)-4]
}
parsedURL, err := giturls.Parse(gitRemote)
if err != nil {
// TODO: log this error
return gitRemote
}
if parsedURL.User == nil || parsedURL.User.Username() == "" {
parsedURL.User = url.User("git")
} }
// TODO: figure out what scheme we want
parsedURL.Scheme = "git+ssh"
return parsedURL.String()
} }

@ -2,269 +2,13 @@ package records
import ( import (
"bufio" "bufio"
"encoding/json"
"fmt"
"io"
"os" "os"
"strconv"
"strings" "strings"
"github.com/curusarn/resh/internal/histlist" "github.com/curusarn/resh/internal/histlist"
"go.uber.org/zap" "go.uber.org/zap"
) )
// BaseRecord - common base for Record and FallbackRecord
type BaseRecord struct {
// core
CmdLine string `json:"cmdLine"`
ExitCode int `json:"exitCode"`
Shell string `json:"shell"`
Uname string `json:"uname"`
SessionID string `json:"sessionId"`
RecordID string `json:"recordId"`
// posix
Home string `json:"home"`
Lang string `json:"lang"`
LcAll string `json:"lcAll"`
Login string `json:"login"`
//Path string `json:"path"`
Pwd string `json:"pwd"`
PwdAfter string `json:"pwdAfter"`
ShellEnv string `json:"shellEnv"`
Term string `json:"term"`
// non-posix"`
RealPwd string `json:"realPwd"`
RealPwdAfter string `json:"realPwdAfter"`
Pid int `json:"pid"`
SessionPID int `json:"sessionPid"`
Host string `json:"host"`
Hosttype string `json:"hosttype"`
Ostype string `json:"ostype"`
Machtype string `json:"machtype"`
Shlvl int `json:"shlvl"`
// before after
TimezoneBefore string `json:"timezoneBefore"`
TimezoneAfter string `json:"timezoneAfter"`
RealtimeBefore float64 `json:"realtimeBefore"`
RealtimeAfter float64 `json:"realtimeAfter"`
RealtimeBeforeLocal float64 `json:"realtimeBeforeLocal"`
RealtimeAfterLocal float64 `json:"realtimeAfterLocal"`
RealtimeDuration float64 `json:"realtimeDuration"`
RealtimeSinceSessionStart float64 `json:"realtimeSinceSessionStart"`
RealtimeSinceBoot float64 `json:"realtimeSinceBoot"`
//Logs []string `json: "logs"`
GitDir string `json:"gitDir"`
GitRealDir string `json:"gitRealDir"`
GitOriginRemote string `json:"gitOriginRemote"`
GitDirAfter string `json:"gitDirAfter"`
GitRealDirAfter string `json:"gitRealDirAfter"`
GitOriginRemoteAfter string `json:"gitOriginRemoteAfter"`
MachineID string `json:"machineId"`
OsReleaseID string `json:"osReleaseId"`
OsReleaseVersionID string `json:"osReleaseVersionId"`
OsReleaseIDLike string `json:"osReleaseIdLike"`
OsReleaseName string `json:"osReleaseName"`
OsReleasePrettyName string `json:"osReleasePrettyName"`
ReshUUID string `json:"reshUuid"`
ReshVersion string `json:"reshVersion"`
ReshRevision string `json:"reshRevision"`
// records come in two parts (collect and postcollect)
PartOne bool `json:"partOne,omitempty"` // false => part two
PartsMerged bool `json:"partsMerged"`
// special flag -> not an actual record but an session end
SessionExit bool `json:"sessionExit,omitempty"`
// recall metadata
Recalled bool `json:"recalled"`
RecallHistno int `json:"recallHistno,omitempty"`
RecallStrategy string `json:"recallStrategy,omitempty"`
RecallActionsRaw string `json:"recallActionsRaw,omitempty"`
RecallActions []string `json:"recallActions,omitempty"`
RecallLastCmdLine string `json:"recallLastCmdLine"`
// recall command
RecallPrefix string `json:"recallPrefix,omitempty"`
// added by sanitizatizer
Sanitized bool `json:"sanitized,omitempty"`
CmdLength int `json:"cmdLength,omitempty"`
}
// Record representing single executed command with its metadata
type Record struct {
BaseRecord
Cols string `json:"cols"`
Lines string `json:"lines"`
}
// EnrichedRecord - record enriched with additional data
type EnrichedRecord struct {
Record
// enriching fields - added "later"
Command string `json:"command"`
FirstWord string `json:"firstWord"`
Invalid bool `json:"invalid"`
SeqSessionID uint64 `json:"seqSessionId"`
LastRecordOfSession bool `json:"lastRecordOfSession"`
DebugThisRecord bool `json:"debugThisRecord"`
Errors []string `json:"errors"`
// SeqSessionID uint64 `json:"seqSessionId,omitempty"`
}
// FallbackRecord when record is too old and can't be parsed into regular Record
type FallbackRecord struct {
BaseRecord
// older version of the record where cols and lines are int
Cols int `json:"cols"` // notice the int type
Lines int `json:"lines"` // notice the int type
}
// Convert from FallbackRecord to Record
func Convert(r *FallbackRecord) Record {
return Record{
BaseRecord: r.BaseRecord,
// these two lines are the only reason we are doing this
Cols: strconv.Itoa(r.Cols),
Lines: strconv.Itoa(r.Lines),
}
}
// ToString - returns record the json
func (r EnrichedRecord) ToString() (string, error) {
jsonRec, err := json.Marshal(r)
if err != nil {
return "marshalling error", err
}
return string(jsonRec), nil
}
// LoadFromFile loads records from 'fname' file
func LoadFromFile(sugar *zap.SugaredLogger, fname string) []Record {
const allowedErrors = 3
var encounteredErrors int
var recs []Record
file, err := os.Open(fname)
if err != nil {
sugar.Error("Failed to open resh history file - skipping reading resh history", zap.Error(err))
return recs
}
defer file.Close()
reader := bufio.NewReader(file)
var i int
for {
var line string
line, err = reader.ReadString('\n')
if err != nil {
break
}
i++
record := Record{}
fallbackRecord := FallbackRecord{}
err = json.Unmarshal([]byte(line), &record)
if err != nil {
err = json.Unmarshal([]byte(line), &fallbackRecord)
if err != nil {
encounteredErrors++
sugar.Error("Could not decode line in resh history file",
"lineContents", line,
"lineNumber", i,
zap.Error(err),
)
if encounteredErrors > allowedErrors {
sugar.Fatal("Encountered too many errors during decoding - exiting",
"allowedErrors", allowedErrors,
)
}
}
record = Convert(&fallbackRecord)
}
recs = append(recs, record)
}
if err != io.EOF {
sugar.Error("Error while loading file", zap.Error(err))
}
sugar.Infow("Loaded resh history records",
"recordCount", len(recs),
)
if encounteredErrors > 0 {
// fix errors in the history file
sugar.Warnw("Some history records could not be decoded - fixing resh history file by dropping them",
"corruptedRecords", encounteredErrors,
)
fnameBak := fname + ".bak"
sugar.Infow("Backing up current corrupted history file",
"backupFilename", fnameBak,
)
err := copyFile(fname, fnameBak)
if err != nil {
sugar.Errorw("Failed to create a backup history file - aborting fixing history file",
"backupFilename", fnameBak,
zap.Error(err),
)
return recs
}
sugar.Info("Writing resh history file without errors ...")
err = writeHistory(fname, recs)
if err != nil {
sugar.Errorw("Failed write fixed history file - aborting fixing history file",
"filename", fname,
zap.Error(err),
)
}
}
return recs
}
func copyFile(source, dest string) error {
from, err := os.Open(source)
if err != nil {
return err
}
defer from.Close()
// to, err := 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 writeHistory(fname string, history []Record) error {
file, err := os.Create(fname)
if err != nil {
return err
}
defer file.Close()
for _, rec := range history {
jsn, err := json.Marshal(rec)
if err != nil {
return fmt.Errorf("failed to encode record: %w", err)
}
file.Write(append(jsn, []byte("\n")...))
}
return nil
}
// LoadCmdLinesFromZshFile loads cmdlines from zsh history file // LoadCmdLinesFromZshFile loads cmdlines from zsh history file
func LoadCmdLinesFromZshFile(sugar *zap.SugaredLogger, fname string) histlist.Histlist { func LoadCmdLinesFromZshFile(sugar *zap.SugaredLogger, fname string) histlist.Histlist {
hl := histlist.New(sugar) hl := histlist.New(sugar)

@ -1,85 +0,0 @@
package records
import (
"bufio"
"encoding/json"
"log"
"os"
"testing"
)
func GetTestRecords() []Record {
file, err := os.Open("testdata/resh_history.json")
if err != nil {
log.Fatalf("Failed to open resh history file: %v", err)
}
defer file.Close()
var recs []Record
scanner := bufio.NewScanner(file)
for scanner.Scan() {
record := Record{}
line := scanner.Text()
err = json.Unmarshal([]byte(line), &record)
if err != nil {
log.Fatalf("Error decoding record: '%s'; err: %v", line, err)
}
recs = append(recs, record)
}
return recs
}
func GetTestEnrichedRecords() []EnrichedRecord {
var recs []EnrichedRecord
for _, rec := range GetTestRecords() {
recs = append(recs, Enriched(rec))
}
return recs
}
func TestToString(t *testing.T) {
for _, rec := range GetTestEnrichedRecords() {
_, err := rec.ToString()
if err != nil {
t.Error("ToString() failed")
}
}
}
func TestEnriched(t *testing.T) {
record := Record{BaseRecord: BaseRecord{CmdLine: "cmd arg1 arg2"}}
enriched := Enriched(record)
if enriched.FirstWord != "cmd" || enriched.Command != "cmd" {
t.Error("Enriched() returned reocord w/ wrong Command OR FirstWord")
}
}
func TestValidate(t *testing.T) {
record := EnrichedRecord{}
if record.Validate() == nil {
t.Error("Validate() didn't return an error for invalid record")
}
record.CmdLine = "cmd arg"
record.FirstWord = "cmd"
record.Command = "cmd"
time := 1234.5678
record.RealtimeBefore = time
record.RealtimeAfter = time
record.RealtimeBeforeLocal = time
record.RealtimeAfterLocal = time
pwd := "/pwd"
record.Pwd = pwd
record.PwdAfter = pwd
record.RealPwd = pwd
record.RealPwdAfter = pwd
if record.Validate() != nil {
t.Error("Validate() returned an error for a valid record")
}
}
func TestGetCommandAndFirstWord(t *testing.T) {
cmd, stWord, err := GetCommandAndFirstWord("cmd arg1 arg2")
if err != nil || cmd != "cmd" || stWord != "cmd" {
t.Error("GetCommandAndFirstWord() returned wrong Command OR FirstWord")
}
}

@ -1,27 +0,0 @@
{"cmdLine":"ls","exitCode":0,"shell":"bash","uname":"Linux","sessionId":"d5c0fe70-c80b-4715-87cb-f8d8d5b4c673","cols":"80","lines":"24","home":"/home/simon","lang":"en_US.UTF-8","lcAll":"","login":"simon","pwd":"/home/simon","pwdAfter":"/home/simon","shellEnv":"/bin/bash","term":"xterm-256color","realPwd":"/home/simon","realPwdAfter":"/home/simon","pid":14560,"sessionPid":14560,"host":"simon-pc","hosttype":"x86_64","ostype":"linux-gnu","machtype":"x86_64-pc-linux-gnu","shlvl":1,"timezoneBefore":"+0200","timezoneAfter":"+0200","realtimeBefore":1566762905.173595,"realtimeAfter":1566762905.1894295,"realtimeBeforeLocal":1566770105.173595,"realtimeAfterLocal":1566770105.1894295,"realtimeDuration":0.015834569931030273,"realtimeSinceSessionStart":1.7122540473937988,"realtimeSinceBoot":20766.542254047396,"gitDir":"","gitRealDir":"","gitOriginRemote":"","machineId":"c70365240bc647f09e2490722cc8186b","osReleaseId":"manjaro","osReleaseVersionId":"","osReleaseIdLike":"arch","osReleaseName":"Manjaro Linux","osReleasePrettyName":"Manjaro Linux","reshUuid":"","reshVersion":"1.1.1","reshRevision":"752acb916f2a"}
{"cmdLine":"find . -name applications","exitCode":0,"shell":"bash","uname":"Linux","sessionId":"c5251955-3a64-4353-952e-08d62a898694","cols":"211","lines":"56","home":"/home/simon","lang":"en_US.UTF-8","lcAll":"","login":"simon","pwd":"/home/simon","pwdAfter":"/home/simon","shellEnv":"/bin/bash","term":"xterm-256color","realPwd":"/home/simon","realPwdAfter":"/home/simon","pid":3109,"sessionPid":3109,"host":"simon-pc","hosttype":"x86_64","ostype":"linux-gnu","machtype":"x86_64-pc-linux-gnu","shlvl":1,"timezoneBefore":"+0200","timezoneAfter":"+0200","realtimeBefore":1567420001.2531302,"realtimeAfter":1567420002.4311218,"realtimeBeforeLocal":1567427201.2531302,"realtimeAfterLocal":1567427202.4311218,"realtimeDuration":1.1779916286468506,"realtimeSinceSessionStart":957.4848053455353,"realtimeSinceBoot":2336.594805345535,"gitDir":"","gitRealDir":"","gitOriginRemote":"","machineId":"c70365240bc647f09e2490722cc8186b","osReleaseId":"manjaro","osReleaseVersionId":"","osReleaseIdLike":"arch","osReleaseName":"Manjaro Linux","osReleasePrettyName":"Manjaro Linux","reshUuid":"","reshVersion":"1.1.1","reshRevision":"752acb916f2a"}
{"cmdLine":"desktop-file-validate curusarn.sync-clipboards.desktop ","exitCode":0,"shell":"bash","uname":"Linux","sessionId":"c5251955-3a64-4353-952e-08d62a898694","cols":"211","lines":"56","home":"/home/simon","lang":"en_US.UTF-8","lcAll":"","login":"simon","pwd":"/home/simon/.local/share/applications","pwdAfter":"/home/simon/.local/share/applications","shellEnv":"/bin/bash","term":"xterm-256color","realPwd":"/home/simon/.local/share/applications","realPwdAfter":"/home/simon/.local/share/applications","pid":3109,"sessionPid":3109,"host":"simon-pc","hosttype":"x86_64","ostype":"linux-gnu","machtype":"x86_64-pc-linux-gnu","shlvl":1,"timezoneBefore":"+0200","timezoneAfter":"+0200","realtimeBefore":1567421748.2965438,"realtimeAfter":1567421748.3068867,"realtimeBeforeLocal":1567428948.2965438,"realtimeAfterLocal":1567428948.3068867,"realtimeDuration":0.010342836380004883,"realtimeSinceSessionStart":2704.528218984604,"realtimeSinceBoot":4083.6382189846036,"gitDir":"","gitRealDir":"","gitOriginRemote":"","machineId":"c70365240bc647f09e2490722cc8186b","osReleaseId":"manjaro","osReleaseVersionId":"","osReleaseIdLike":"arch","osReleaseName":"Manjaro Linux","osReleasePrettyName":"Manjaro Linux","reshUuid":"","reshVersion":"1.1.1","reshRevision":"752acb916f2a"}
{"cmdLine":"cat /tmp/extensions | grep '.'","exitCode":0,"shell":"bash","uname":"Linux","sessionId":"f044cdbf-fd51-4c37-8528-dcd98fc7b6d9","cols":"211","lines":"56","home":"/home/simon","lang":"en_US.UTF-8","lcAll":"","login":"simon","pwd":"/home/simon","pwdAfter":"/home/simon","shellEnv":"/bin/bash","term":"xterm-256color","realPwd":"/home/simon","realPwdAfter":"/home/simon","pid":6887,"sessionPid":6887,"host":"simon-pc","hosttype":"x86_64","ostype":"linux-gnu","machtype":"x86_64-pc-linux-gnu","shlvl":1,"timezoneBefore":"+0200","timezoneAfter":"+0200","realtimeBefore":1567461416.6871984,"realtimeAfter":1567461416.7336714,"realtimeBeforeLocal":1567468616.6871984,"realtimeAfterLocal":1567468616.7336714,"realtimeDuration":0.046473026275634766,"realtimeSinceSessionStart":21.45597553253174,"realtimeSinceBoot":43752.03597553253,"gitDir":"","gitRealDir":"","gitOriginRemote":"","machineId":"c70365240bc647f09e2490722cc8186b","osReleaseId":"manjaro","osReleaseVersionId":"","osReleaseIdLike":"arch","osReleaseName":"Manjaro Linux","osReleasePrettyName":"Manjaro Linux","reshUuid":"","reshVersion":"1.1.1","reshRevision":"752acb916f2a"}
{"cmdLine":"cd git/resh/","exitCode":0,"shell":"bash","uname":"Linux","sessionId":"f044cdbf-fd51-4c37-8528-dcd98fc7b6d9","cols":"211","lines":"56","home":"/home/simon","lang":"en_US.UTF-8","lcAll":"","login":"simon","pwd":"/home/simon","pwdAfter":"/home/simon/git/resh","shellEnv":"/bin/bash","term":"xterm-256color","realPwd":"/home/simon","realPwdAfter":"/home/simon/git/resh","pid":6887,"sessionPid":6887,"host":"simon-pc","hosttype":"x86_64","ostype":"linux-gnu","machtype":"x86_64-pc-linux-gnu","shlvl":1,"timezoneBefore":"+0200","timezoneAfter":"+0200","realtimeBefore":1567461667.8806899,"realtimeAfter":1567461667.8949044,"realtimeBeforeLocal":1567468867.8806899,"realtimeAfterLocal":1567468867.8949044,"realtimeDuration":0.014214515686035156,"realtimeSinceSessionStart":272.64946699142456,"realtimeSinceBoot":44003.229466991426,"gitDir":"","gitRealDir":"","gitOriginRemote":"","machineId":"c70365240bc647f09e2490722cc8186b","osReleaseId":"manjaro","osReleaseVersionId":"","osReleaseIdLike":"arch","osReleaseName":"Manjaro Linux","osReleasePrettyName":"Manjaro Linux","reshUuid":"","reshVersion":"1.1.1","reshRevision":"752acb916f2a"}
{"cmdLine":"git s","exitCode":0,"shell":"bash","uname":"Linux","sessionId":"f044cdbf-fd51-4c37-8528-dcd98fc7b6d9","cols":"211","lines":"56","home":"/home/simon","lang":"en_US.UTF-8","lcAll":"","login":"simon","pwd":"/home/simon/git/resh","pwdAfter":"/home/simon/git/resh","shellEnv":"/bin/bash","term":"xterm-256color","realPwd":"/home/simon/git/resh","realPwdAfter":"/home/simon/git/resh","pid":6887,"sessionPid":6887,"host":"simon-pc","hosttype":"x86_64","ostype":"linux-gnu","machtype":"x86_64-pc-linux-gnu","shlvl":1,"timezoneBefore":"+0200","timezoneAfter":"+0200","realtimeBefore":1567461707.6467602,"realtimeAfter":1567461707.7177293,"realtimeBeforeLocal":1567468907.6467602,"realtimeAfterLocal":1567468907.7177293,"realtimeDuration":0.0709691047668457,"realtimeSinceSessionStart":312.4155373573303,"realtimeSinceBoot":44042.99553735733,"gitDir":"/home/simon/git/resh","gitRealDir":"/home/simon/git/resh","gitOriginRemote":"git@github.com:curusarn/resh.git","machineId":"c70365240bc647f09e2490722cc8186b","osReleaseId":"manjaro","osReleaseVersionId":"","osReleaseIdLike":"arch","osReleaseName":"Manjaro Linux","osReleasePrettyName":"Manjaro Linux","reshUuid":"","reshVersion":"1.1.1","reshRevision":"752acb916f2a"}
{"cmdLine":"cat /tmp/extensions | grep '^\\.' | cut -f1 |tr '[:upper:]' '[:lower:]' ","exitCode":0,"shell":"bash","uname":"Linux","sessionId":"f044cdbf-fd51-4c37-8528-dcd98fc7b6d9","cols":"211","lines":"56","home":"/home/simon","lang":"en_US.UTF-8","lcAll":"","login":"simon","pwd":"/home/simon/git/resh","pwdAfter":"/home/simon/git/resh","shellEnv":"/bin/bash","term":"xterm-256color","realPwd":"/home/simon/git/resh","realPwdAfter":"/home/simon/git/resh","pid":6887,"sessionPid":6887,"host":"simon-pc","hosttype":"x86_64","ostype":"linux-gnu","machtype":"x86_64-pc-linux-gnu","shlvl":1,"timezoneBefore":"+0200","timezoneAfter":"+0200","realtimeBefore":1567461722.813049,"realtimeAfter":1567461722.8280325,"realtimeBeforeLocal":1567468922.813049,"realtimeAfterLocal":1567468922.8280325,"realtimeDuration":0.014983415603637695,"realtimeSinceSessionStart":327.581826210022,"realtimeSinceBoot":44058.161826210024,"gitDir":"/home/simon/git/resh","gitRealDir":"/home/simon/git/resh","gitOriginRemote":"git@github.com:curusarn/resh.git","machineId":"c70365240bc647f09e2490722cc8186b","osReleaseId":"manjaro","osReleaseVersionId":"","osReleaseIdLike":"arch","osReleaseName":"Manjaro Linux","osReleasePrettyName":"Manjaro Linux","reshUuid":"","reshVersion":"1.1.1","reshRevision":"752acb916f2a"}
{"cmdLine":"tig","exitCode":127,"shell":"bash","uname":"Linux","sessionId":"f044cdbf-fd51-4c37-8528-dcd98fc7b6d9","cols":"211","lines":"56","home":"/home/simon","lang":"en_US.UTF-8","lcAll":"","login":"simon","pwd":"/home/simon/git/resh","pwdAfter":"/home/simon/git/resh","shellEnv":"/bin/bash","term":"xterm-256color","realPwd":"/home/simon/git/resh","realPwdAfter":"/home/simon/git/resh","pid":6887,"sessionPid":6887,"host":"simon-pc","hosttype":"x86_64","ostype":"linux-gnu","machtype":"x86_64-pc-linux-gnu","shlvl":1,"timezoneBefore":"+0200","timezoneAfter":"+0200","realtimeBefore":1567461906.3896828,"realtimeAfter":1567461906.4084594,"realtimeBeforeLocal":1567469106.3896828,"realtimeAfterLocal":1567469106.4084594,"realtimeDuration":0.018776655197143555,"realtimeSinceSessionStart":511.1584599018097,"realtimeSinceBoot":44241.73845990181,"gitDir":"/home/simon/git/resh","gitRealDir":"/home/simon/git/resh","gitOriginRemote":"git@github.com:curusarn/resh.git","machineId":"c70365240bc647f09e2490722cc8186b","osReleaseId":"manjaro","osReleaseVersionId":"","osReleaseIdLike":"arch","osReleaseName":"Manjaro Linux","osReleasePrettyName":"Manjaro Linux","reshUuid":"","reshVersion":"1.1.1","reshRevision":"752acb916f2a"}
{"cmdLine":"resh-sanitize-history | jq","exitCode":0,"shell":"bash","uname":"Linux","sessionId":"a3318c80-3521-4b22-aa64-ea0f6c641410","cols":"211","lines":"56","home":"/home/simon","lang":"en_US.UTF-8","lcAll":"","login":"simon","pwd":"/home/simon","pwdAfter":"/home/simon","shellEnv":"/bin/bash","term":"xterm-256color","realPwd":"/home/simon","realPwdAfter":"/home/simon","pid":14601,"sessionPid":14601,"host":"simon-pc","hosttype":"x86_64","ostype":"linux-gnu","machtype":"x86_64-pc-linux-gnu","shlvl":1,"timezoneBefore":"+0200","timezoneAfter":"+0200","realtimeBefore":1567547116.2430356,"realtimeAfter":1567547116.7547352,"realtimeBeforeLocal":1567554316.2430356,"realtimeAfterLocal":1567554316.7547352,"realtimeDuration":0.5116996765136719,"realtimeSinceSessionStart":15.841878414154053,"realtimeSinceBoot":30527.201878414155,"gitDir":"","gitRealDir":"","gitOriginRemote":"","machineId":"c70365240bc647f09e2490722cc8186b","osReleaseId":"manjaro","osReleaseVersionId":"","osReleaseIdLike":"arch","osReleaseName":"Manjaro Linux","osReleasePrettyName":"Manjaro Linux","reshUuid":"","reshVersion":"1.1.1","reshRevision":"737bc0a4df38","cmdLength":0}
{"cmdLine":"sudo pacman -S ansible","exitCode":0,"shell":"bash","uname":"Linux","sessionId":"64154f2d-a4bc-4463-a690-520080b61ead","cols":"211","lines":"56","home":"/home/simon","lang":"en_US.UTF-8","lcAll":"","login":"simon","pwd":"/home/simon/git/kristin","pwdAfter":"/home/simon/git/kristin","shellEnv":"/bin/bash","term":"xterm-256color","realPwd":"/home/simon/git/kristin","realPwdAfter":"/home/simon/git/kristin","pid":5663,"sessionPid":5663,"host":"simon-pc","hosttype":"x86_64","ostype":"linux-gnu","machtype":"x86_64-pc-linux-gnu","shlvl":1,"timezoneBefore":"+0200","timezoneAfter":"+0200","realtimeBefore":1567609042.0166302,"realtimeAfter":1567609076.9726007,"realtimeBeforeLocal":1567616242.0166302,"realtimeAfterLocal":1567616276.9726007,"realtimeDuration":34.95597052574158,"realtimeSinceSessionStart":1617.0794131755829,"realtimeSinceBoot":6120.029413175583,"gitDir":"/home/simon/git/kristin","gitRealDir":"/home/simon/git/kristin","gitOriginRemote":"git@gitlab.com:sucvut/kristin.git","machineId":"c70365240bc647f09e2490722cc8186b","osReleaseId":"manjaro","osReleaseVersionId":"","osReleaseIdLike":"arch","osReleaseName":"Manjaro Linux","osReleasePrettyName":"Manjaro Linux","reshUuid":"","reshVersion":"1.1.1","reshRevision":"737bc0a4df38","cmdLength":0}
{"cmdLine":"vagrant up","exitCode":1,"shell":"bash","uname":"Linux","sessionId":"64154f2d-a4bc-4463-a690-520080b61ead","cols":"211","lines":"56","home":"/home/simon","lang":"en_US.UTF-8","lcAll":"","login":"simon","pwd":"/home/simon/git/kristin","pwdAfter":"/home/simon/git/kristin","shellEnv":"/bin/bash","term":"xterm-256color","realPwd":"/home/simon/git/kristin","realPwdAfter":"/home/simon/git/kristin","pid":5663,"sessionPid":5663,"host":"simon-pc","hosttype":"x86_64","ostype":"linux-gnu","machtype":"x86_64-pc-linux-gnu","shlvl":1,"timezoneBefore":"+0200","timezoneAfter":"+0200","realtimeBefore":1567609090.7359188,"realtimeAfter":1567609098.3125577,"realtimeBeforeLocal":1567616290.7359188,"realtimeAfterLocal":1567616298.3125577,"realtimeDuration":7.57663893699646,"realtimeSinceSessionStart":1665.798701763153,"realtimeSinceBoot":6168.748701763153,"gitDir":"/home/simon/git/kristin","gitRealDir":"/home/simon/git/kristin","gitOriginRemote":"git@gitlab.com:sucvut/kristin.git","machineId":"c70365240bc647f09e2490722cc8186b","osReleaseId":"manjaro","osReleaseVersionId":"","osReleaseIdLike":"arch","osReleaseName":"Manjaro Linux","osReleasePrettyName":"Manjaro Linux","reshUuid":"","reshVersion":"1.1.1","reshRevision":"737bc0a4df38","cmdLength":0}
{"cmdLine":"sudo modprobe vboxnetflt","exitCode":0,"shell":"bash","uname":"Linux","sessionId":"64154f2d-a4bc-4463-a690-520080b61ead","cols":"211","lines":"56","home":"/home/simon","lang":"en_US.UTF-8","lcAll":"","login":"simon","pwd":"/home/simon/git/kristin","pwdAfter":"/home/simon/git/kristin","shellEnv":"/bin/bash","term":"xterm-256color","realPwd":"/home/simon/git/kristin","realPwdAfter":"/home/simon/git/kristin","pid":5663,"sessionPid":5663,"host":"simon-pc","hosttype":"x86_64","ostype":"linux-gnu","machtype":"x86_64-pc-linux-gnu","shlvl":1,"timezoneBefore":"+0200","timezoneAfter":"+0200","realtimeBefore":1567609143.2847652,"realtimeAfter":1567609143.3116078,"realtimeBeforeLocal":1567616343.2847652,"realtimeAfterLocal":1567616343.3116078,"realtimeDuration":0.026842594146728516,"realtimeSinceSessionStart":1718.3475482463837,"realtimeSinceBoot":6221.2975482463835,"gitDir":"/home/simon/git/kristin","gitRealDir":"/home/simon/git/kristin","gitOriginRemote":"git@gitlab.com:sucvut/kristin.git","machineId":"c70365240bc647f09e2490722cc8186b","osReleaseId":"manjaro","osReleaseVersionId":"","osReleaseIdLike":"arch","osReleaseName":"Manjaro Linux","osReleasePrettyName":"Manjaro Linux","reshUuid":"","reshVersion":"1.1.1","reshRevision":"737bc0a4df38","cmdLength":0}
{"cmdLine":"echo $RANDOM","exitCode":0,"shell":"bash","uname":"Linux","sessionId":"8ddacadc-6e73-483c-b347-4e18df204466","cols":"211","lines":"56","home":"/home/simon","lang":"en_US.UTF-8","lcAll":"","login":"simon","pwd":"/home/simon","pwdAfter":"/home/simon","shellEnv":"/bin/bash","term":"xterm-256color","realPwd":"/home/simon","realPwdAfter":"/home/simon","pid":31387,"sessionPid":31387,"host":"simon-pc","hosttype":"x86_64","ostype":"linux-gnu","machtype":"x86_64-pc-linux-gnu","shlvl":1,"timezoneBefore":"+0200","timezoneAfter":"+0200","realtimeBefore":1567727039.6540458,"realtimeAfter":1567727039.6629689,"realtimeBeforeLocal":1567734239.6540458,"realtimeAfterLocal":1567734239.6629689,"realtimeDuration":0.008923053741455078,"realtimeSinceSessionStart":1470.7667458057404,"realtimeSinceBoot":18495.01674580574,"gitDir":"","gitRealDir":"","gitOriginRemote":"","machineId":"c70365240bc647f09e2490722cc8186b","osReleaseId":"manjaro","osReleaseVersionId":"","osReleaseIdLike":"arch","osReleaseName":"Manjaro Linux","osReleasePrettyName":"Manjaro Linux","reshUuid":"","reshVersion":"1.1.1","reshRevision":"737bc0a4df38","cmdLength":0}
{"cmdLine":"make resh-evaluate ","exitCode":0,"shell":"bash","uname":"Linux","sessionId":"93998b68-ec48-4e48-9e4a-b37b39f5439e","cols":"211","lines":"56","home":"/home/simon","lang":"en_US.UTF-8","lcAll":"","login":"simon","pwd":"/home/simon/git/resh","pwdAfter":"/home/simon/git/resh","shellEnv":"/bin/bash","term":"xterm-256color","realPwd":"/home/simon/git/resh","realPwdAfter":"/home/simon/git/resh","pid":9463,"sessionPid":9463,"host":"simon-pc","hosttype":"x86_64","ostype":"linux-gnu","machtype":"x86_64-pc-linux-gnu","shlvl":1,"timezoneBefore":"+0200","timezoneAfter":"+0200","realtimeBefore":1567977478.9672194,"realtimeAfter":1567977479.5449634,"realtimeBeforeLocal":1567984678.9672194,"realtimeAfterLocal":1567984679.5449634,"realtimeDuration":0.5777440071105957,"realtimeSinceSessionStart":5738.577540636063,"realtimeSinceBoot":20980.42754063606,"gitDir":"/home/simon/git/resh","gitRealDir":"/home/simon/git/resh","gitOriginRemote":"git@github.com:curusarn/resh.git","machineId":"c70365240bc647f09e2490722cc8186b","osReleaseId":"manjaro","osReleaseVersionId":"","osReleaseIdLike":"arch","osReleaseName":"Manjaro Linux","osReleasePrettyName":"Manjaro Linux","reshUuid":"","reshVersion":"1.1.1","reshRevision":"737bc0a4df38","cmdLength":0}
{"cmdLine":"cat ~/.resh_history.json | grep \"./resh-eval\" | jq","exitCode":0,"shell":"bash","uname":"Linux","sessionId":"93998b68-ec48-4e48-9e4a-b37b39f5439e","cols":"105","lines":"56","home":"/home/simon","lang":"en_US.UTF-8","lcAll":"","login":"simon","pwd":"/home/simon/git/resh","pwdAfter":"/home/simon/git/resh","shellEnv":"/bin/bash","term":"xterm-256color","realPwd":"/home/simon/git/resh","realPwdAfter":"/home/simon/git/resh","pid":9463,"sessionPid":9463,"host":"simon-pc","hosttype":"x86_64","ostype":"linux-gnu","machtype":"x86_64-pc-linux-gnu","shlvl":1,"timezoneBefore":"+0200","timezoneAfter":"+0200","realtimeBefore":1567986105.3988302,"realtimeAfter":1567986105.4809113,"realtimeBeforeLocal":1567993305.3988302,"realtimeAfterLocal":1567993305.4809113,"realtimeDuration":0.08208107948303223,"realtimeSinceSessionStart":14365.00915145874,"realtimeSinceBoot":29606.85915145874,"gitDir":"/home/simon/git/resh","gitRealDir":"/home/simon/git/resh","gitOriginRemote":"git@github.com:curusarn/resh.git","machineId":"c70365240bc647f09e2490722cc8186b","osReleaseId":"manjaro","osReleaseVersionId":"","osReleaseIdLike":"arch","osReleaseName":"Manjaro Linux","osReleasePrettyName":"Manjaro Linux","reshUuid":"","reshVersion":"1.1.1","reshRevision":"737bc0a4df38","cmdLength":0}
{"cmdLine":"git c \"add sanitized flag to record, add Enrich() to record\"","exitCode":0,"shell":"bash","uname":"Linux","sessionId":"93998b68-ec48-4e48-9e4a-b37b39f5439e","cols":"211","lines":"56","home":"/home/simon","lang":"en_US.UTF-8","lcAll":"","login":"simon","pwd":"/home/simon/git/resh","pwdAfter":"/home/simon/git/resh","shellEnv":"/bin/bash","term":"xterm-256color","realPwd":"/home/simon/git/resh","realPwdAfter":"/home/simon/git/resh","pid":9463,"sessionPid":9463,"host":"simon-pc","hosttype":"x86_64","ostype":"linux-gnu","machtype":"x86_64-pc-linux-gnu","shlvl":1,"timezoneBefore":"+0200","timezoneAfter":"+0200","realtimeBefore":1568063976.9103937,"realtimeAfter":1568063976.9326868,"realtimeBeforeLocal":1568071176.9103937,"realtimeAfterLocal":1568071176.9326868,"realtimeDuration":0.0222930908203125,"realtimeSinceSessionStart":92236.52071499825,"realtimeSinceBoot":107478.37071499825,"gitDir":"/home/simon/git/resh","gitRealDir":"/home/simon/git/resh","gitOriginRemote":"git@github.com:curusarn/resh.git","machineId":"c70365240bc647f09e2490722cc8186b","osReleaseId":"manjaro","osReleaseVersionId":"","osReleaseIdLike":"arch","osReleaseName":"Manjaro Linux","osReleasePrettyName":"Manjaro Linux","reshUuid":"","reshVersion":"1.1.1","reshRevision":"737bc0a4df38","cmdLength":0}
{"cmdLine":"git s","exitCode":0,"shell":"bash","uname":"Linux","sessionId":"93998b68-ec48-4e48-9e4a-b37b39f5439e","cols":"211","lines":"56","home":"/home/simon","lang":"en_US.UTF-8","lcAll":"","login":"simon","pwd":"/home/simon/git/resh","pwdAfter":"/home/simon/git/resh","shellEnv":"/bin/bash","term":"xterm-256color","realPwd":"/home/simon/git/resh","realPwdAfter":"/home/simon/git/resh","pid":9463,"sessionPid":9463,"host":"simon-pc","hosttype":"x86_64","ostype":"linux-gnu","machtype":"x86_64-pc-linux-gnu","shlvl":1,"timezoneBefore":"+0200","timezoneAfter":"+0200","realtimeBefore":1568063978.2340608,"realtimeAfter":1568063978.252463,"realtimeBeforeLocal":1568071178.2340608,"realtimeAfterLocal":1568071178.252463,"realtimeDuration":0.0184023380279541,"realtimeSinceSessionStart":92237.84438204765,"realtimeSinceBoot":107479.69438204766,"gitDir":"/home/simon/git/resh","gitRealDir":"/home/simon/git/resh","gitOriginRemote":"git@github.com:curusarn/resh.git","machineId":"c70365240bc647f09e2490722cc8186b","osReleaseId":"manjaro","osReleaseVersionId":"","osReleaseIdLike":"arch","osReleaseName":"Manjaro Linux","osReleasePrettyName":"Manjaro Linux","reshUuid":"","reshVersion":"1.1.1","reshRevision":"737bc0a4df38","cmdLength":0}
{"cmdLine":"git a evaluate/results.go ","exitCode":0,"shell":"bash","uname":"Linux","sessionId":"93998b68-ec48-4e48-9e4a-b37b39f5439e","cols":"211","lines":"56","home":"/home/simon","lang":"en_US.UTF-8","lcAll":"","login":"simon","pwd":"/home/simon/git/resh","pwdAfter":"/home/simon/git/resh","shellEnv":"/bin/bash","term":"xterm-256color","realPwd":"/home/simon/git/resh","realPwdAfter":"/home/simon/git/resh","pid":9463,"sessionPid":9463,"host":"simon-pc","hosttype":"x86_64","ostype":"linux-gnu","machtype":"x86_64-pc-linux-gnu","shlvl":1,"timezoneBefore":"+0200","timezoneAfter":"+0200","realtimeBefore":1568063989.0446353,"realtimeAfter":1568063989.2452207,"realtimeBeforeLocal":1568071189.0446353,"realtimeAfterLocal":1568071189.2452207,"realtimeDuration":0.20058536529541016,"realtimeSinceSessionStart":92248.65495657921,"realtimeSinceBoot":107490.50495657921,"gitDir":"/home/simon/git/resh","gitRealDir":"/home/simon/git/resh","gitOriginRemote":"git@github.com:curusarn/resh.git","machineId":"c70365240bc647f09e2490722cc8186b","osReleaseId":"manjaro","osReleaseVersionId":"","osReleaseIdLike":"arch","osReleaseName":"Manjaro Linux","osReleasePrettyName":"Manjaro Linux","reshUuid":"","reshVersion":"1.1.1","reshRevision":"737bc0a4df38","cmdLength":0}
{"cmdLine":"sudo pacman -S python-pip","exitCode":0,"shell":"bash","uname":"Linux","sessionId":"93998b68-ec48-4e48-9e4a-b37b39f5439e","cols":"211","lines":"56","home":"/home/simon","lang":"en_US.UTF-8","lcAll":"","login":"simon","pwd":"/home/simon/git/resh","pwdAfter":"/home/simon/git/resh","shellEnv":"/bin/bash","term":"xterm-256color","realPwd":"/home/simon/git/resh","realPwdAfter":"/home/simon/git/resh","pid":9463,"sessionPid":9463,"host":"simon-pc","hosttype":"x86_64","ostype":"linux-gnu","machtype":"x86_64-pc-linux-gnu","shlvl":1,"timezoneBefore":"+0200","timezoneAfter":"+0200","realtimeBefore":1568072068.3557143,"realtimeAfter":1568072070.7509863,"realtimeBeforeLocal":1568079268.3557143,"realtimeAfterLocal":1568079270.7509863,"realtimeDuration":2.3952720165252686,"realtimeSinceSessionStart":100327.96603560448,"realtimeSinceBoot":115569.81603560448,"gitDir":"/home/simon/git/resh","gitRealDir":"/home/simon/git/resh","gitOriginRemote":"git@github.com:curusarn/resh.git","machineId":"c70365240bc647f09e2490722cc8186b","osReleaseId":"manjaro","osReleaseVersionId":"","osReleaseIdLike":"arch","osReleaseName":"Manjaro Linux","osReleasePrettyName":"Manjaro Linux","reshUuid":"","reshVersion":"1.1.1","reshRevision":"737bc0a4df38","cmdLength":0}
{"cmdLine":"pip3 install matplotlib","exitCode":1,"shell":"bash","uname":"Linux","sessionId":"93998b68-ec48-4e48-9e4a-b37b39f5439e","cols":"211","lines":"56","home":"/home/simon","lang":"en_US.UTF-8","lcAll":"","login":"simon","pwd":"/home/simon/git/resh","pwdAfter":"/home/simon/git/resh","shellEnv":"/bin/bash","term":"xterm-256color","realPwd":"/home/simon/git/resh","realPwdAfter":"/home/simon/git/resh","pid":9463,"sessionPid":9463,"host":"simon-pc","hosttype":"x86_64","ostype":"linux-gnu","machtype":"x86_64-pc-linux-gnu","shlvl":1,"timezoneBefore":"+0200","timezoneAfter":"+0200","realtimeBefore":1568072088.5575967,"realtimeAfter":1568072094.372314,"realtimeBeforeLocal":1568079288.5575967,"realtimeAfterLocal":1568079294.372314,"realtimeDuration":5.8147172927856445,"realtimeSinceSessionStart":100348.16791796684,"realtimeSinceBoot":115590.01791796685,"gitDir":"/home/simon/git/resh","gitRealDir":"/home/simon/git/resh","gitOriginRemote":"git@github.com:curusarn/resh.git","machineId":"c70365240bc647f09e2490722cc8186b","osReleaseId":"manjaro","osReleaseVersionId":"","osReleaseIdLike":"arch","osReleaseName":"Manjaro Linux","osReleasePrettyName":"Manjaro Linux","reshUuid":"","reshVersion":"1.1.1","reshRevision":"737bc0a4df38","cmdLength":0}
{"cmdLine":"sudo pip3 install matplotlib","exitCode":0,"shell":"bash","uname":"Linux","sessionId":"93998b68-ec48-4e48-9e4a-b37b39f5439e","cols":"211","lines":"56","home":"/home/simon","lang":"en_US.UTF-8","lcAll":"","login":"simon","pwd":"/home/simon/git/resh","pwdAfter":"/home/simon/git/resh","shellEnv":"/bin/bash","term":"xterm-256color","realPwd":"/home/simon/git/resh","realPwdAfter":"/home/simon/git/resh","pid":9463,"sessionPid":9463,"host":"simon-pc","hosttype":"x86_64","ostype":"linux-gnu","machtype":"x86_64-pc-linux-gnu","shlvl":1,"timezoneBefore":"+0200","timezoneAfter":"+0200","realtimeBefore":1568072106.138616,"realtimeAfter":1568072115.1124601,"realtimeBeforeLocal":1568079306.138616,"realtimeAfterLocal":1568079315.1124601,"realtimeDuration":8.973844051361084,"realtimeSinceSessionStart":100365.7489373684,"realtimeSinceBoot":115607.5989373684,"gitDir":"/home/simon/git/resh","gitRealDir":"/home/simon/git/resh","gitOriginRemote":"git@github.com:curusarn/resh.git","machineId":"c70365240bc647f09e2490722cc8186b","osReleaseId":"manjaro","osReleaseVersionId":"","osReleaseIdLike":"arch","osReleaseName":"Manjaro Linux","osReleasePrettyName":"Manjaro Linux","reshUuid":"","reshVersion":"1.1.1","reshRevision":"737bc0a4df38","cmdLength":0}
{"cmdLine":"./resh-evaluate --plotting-script evaluate/resh-evaluate-plot.py --input ~/git/resh_private/history_data/simon/dell/resh_history.json ","exitCode":130,"shell":"bash","uname":"Linux","sessionId":"93998b68-ec48-4e48-9e4a-b37b39f5439e","cols":"211","lines":"56","home":"/home/simon","lang":"en_US.UTF-8","lcAll":"","login":"simon","pwd":"/home/simon/git/resh","pwdAfter":"/home/simon/git/resh","shellEnv":"/bin/bash","term":"xterm-256color","realPwd":"/home/simon/git/resh","realPwdAfter":"/home/simon/git/resh","pid":9463,"sessionPid":9463,"host":"simon-pc","hosttype":"x86_64","ostype":"linux-gnu","machtype":"x86_64-pc-linux-gnu","shlvl":1,"timezoneBefore":"+0200","timezoneAfter":"+0200","realtimeBefore":1568076266.9364285,"realtimeAfter":1568076288.1131275,"realtimeBeforeLocal":1568083466.9364285,"realtimeAfterLocal":1568083488.1131275,"realtimeDuration":21.176698923110962,"realtimeSinceSessionStart":104526.54674983025,"realtimeSinceBoot":119768.39674983025,"gitDir":"/home/simon/git/resh","gitRealDir":"/home/simon/git/resh","gitOriginRemote":"git@github.com:curusarn/resh.git","machineId":"c70365240bc647f09e2490722cc8186b","osReleaseId":"manjaro","osReleaseVersionId":"","osReleaseIdLike":"arch","osReleaseName":"Manjaro Linux","osReleasePrettyName":"Manjaro Linux","reshUuid":"","reshVersion":"1.1.1","reshRevision":"737bc0a4df38","cmdLength":0}
{"cmdLine":"git c \"Add a bunch of useless comments to make linter happy\"","exitCode":0,"shell":"bash","uname":"Linux","sessionId":"04050353-a97d-4435-9248-f47dd08b2f2a","cols":"211","lines":"56","home":"/home/simon","lang":"en_US.UTF-8","lcAll":"","login":"simon","pwd":"/home/simon/git/resh","pwdAfter":"/home/simon/git/resh","shellEnv":"/bin/bash","term":"xterm-256color","realPwd":"/home/simon/git/resh","realPwdAfter":"/home/simon/git/resh","pid":14702,"sessionPid":14702,"host":"simon-pc","hosttype":"x86_64","ostype":"linux-gnu","machtype":"x86_64-pc-linux-gnu","shlvl":1,"timezoneBefore":"+0200","timezoneAfter":"+0200","realtimeBefore":1569456045.8763022,"realtimeAfter":1569456045.9030173,"realtimeBeforeLocal":1569463245.8763022,"realtimeAfterLocal":1569463245.9030173,"realtimeDuration":0.02671504020690918,"realtimeSinceSessionStart":2289.789242744446,"realtimeSinceBoot":143217.91924274445,"gitDir":"/home/simon/git/resh","gitRealDir":"/home/simon/git/resh","gitOriginRemote":"git@github.com:curusarn/resh.git","machineId":"c70365240bc647f09e2490722cc8186b","osReleaseId":"manjaro","osReleaseVersionId":"","osReleaseIdLike":"arch","osReleaseName":"Manjaro Linux","osReleasePrettyName":"Manjaro Linux","reshUuid":"","reshVersion":"1.1.3","reshRevision":"188d8b420493","sanitized":false}
{"cmdLine":"fuck","exitCode":0,"shell":"bash","uname":"Linux","sessionId":"a4aadf03-610d-4731-ba94-5b7ce21e7bb9","cols":"211","lines":"56","home":"/home/simon","lang":"en_US.UTF-8","lcAll":"","login":"simon","pwd":"/home/simon/git/resh","pwdAfter":"/home/simon/git/resh","shellEnv":"/bin/bash","term":"xterm-256color","realPwd":"/home/simon/git/resh","realPwdAfter":"/home/simon/git/resh","pid":3413,"sessionPid":3413,"host":"simon-pc","hosttype":"x86_64","ostype":"linux-gnu","machtype":"x86_64-pc-linux-gnu","shlvl":1,"timezoneBefore":"+0200","timezoneAfter":"+0200","realtimeBefore":1569687682.4250975,"realtimeAfter":1569687682.5877323,"realtimeBeforeLocal":1569694882.4250975,"realtimeAfterLocal":1569694882.5877323,"realtimeDuration":0.16263484954833984,"realtimeSinceSessionStart":264603.49496507645,"realtimeSinceBoot":374854.48496507644,"gitDir":"/home/simon/git/resh","gitRealDir":"/home/simon/git/resh","gitOriginRemote":"git@github.com:curusarn/resh.git","machineId":"c70365240bc647f09e2490722cc8186b","osReleaseId":"manjaro","osReleaseVersionId":"","osReleaseIdLike":"arch","osReleaseName":"Manjaro Linux","osReleasePrettyName":"Manjaro Linux","reshUuid":"","reshVersion":"1.1.3","reshRevision":"188d8b420493","sanitized":false}
{"cmdLine":"code .","exitCode":0,"shell":"bash","uname":"Linux","sessionId":"87c7ab14-ae51-408d-adbc-fc4f9d28de6e","cols":"211","lines":"56","home":"/home/simon","lang":"en_US.UTF-8","lcAll":"","login":"simon","pwd":"/home/simon/git/resh","pwdAfter":"/home/simon/git/resh","shellEnv":"/bin/bash","term":"xterm-256color","realPwd":"/home/simon/git/resh","realPwdAfter":"/home/simon/git/resh","pid":31947,"sessionPid":31947,"host":"simon-pc","hosttype":"x86_64","ostype":"linux-gnu","machtype":"x86_64-pc-linux-gnu","shlvl":1,"timezoneBefore":"+0200","timezoneAfter":"+0200","realtimeBefore":1569709366.523767,"realtimeAfter":1569709367.516908,"realtimeBeforeLocal":1569716566.523767,"realtimeAfterLocal":1569716567.516908,"realtimeDuration":0.9931409358978271,"realtimeSinceSessionStart":23846.908839941025,"realtimeSinceBoot":396539.888839941,"gitDir":"/home/simon/git/resh","gitRealDir":"/home/simon/git/resh","gitOriginRemote":"git@github.com:curusarn/resh.git","machineId":"c70365240bc647f09e2490722cc8186b","osReleaseId":"manjaro","osReleaseVersionId":"","osReleaseIdLike":"arch","osReleaseName":"Manjaro Linux","osReleasePrettyName":"Manjaro Linux","reshUuid":"","reshVersion":"1.1.3","reshRevision":"188d8b420493","sanitized":false}
{"cmdLine":"make test","exitCode":0,"shell":"bash","uname":"Linux","sessionId":"87c7ab14-ae51-408d-adbc-fc4f9d28de6e","cols":"211","lines":"56","home":"/home/simon","lang":"en_US.UTF-8","lcAll":"","login":"simon","pwd":"/home/simon/git/resh","pwdAfter":"/home/simon/git/resh","shellEnv":"/bin/bash","term":"xterm-256color","realPwd":"/home/simon/git/resh","realPwdAfter":"/home/simon/git/resh","pid":31947,"sessionPid":31947,"host":"simon-pc","hosttype":"x86_64","ostype":"linux-gnu","machtype":"x86_64-pc-linux-gnu","shlvl":1,"timezoneBefore":"+0200","timezoneAfter":"+0200","realtimeBefore":1569709371.89966,"realtimeAfter":1569709377.430194,"realtimeBeforeLocal":1569716571.89966,"realtimeAfterLocal":1569716577.430194,"realtimeDuration":5.530533790588379,"realtimeSinceSessionStart":23852.284733057022,"realtimeSinceBoot":396545.264733057,"gitDir":"/home/simon/git/resh","gitRealDir":"/home/simon/git/resh","gitOriginRemote":"git@github.com:curusarn/resh.git","machineId":"c70365240bc647f09e2490722cc8186b","osReleaseId":"manjaro","osReleaseVersionId":"","osReleaseIdLike":"arch","osReleaseName":"Manjaro Linux","osReleasePrettyName":"Manjaro Linux","reshUuid":"","reshVersion":"1.1.3","reshRevision":"188d8b420493","sanitized":false}
{"cmdLine":"mkdir ~/git/resh/testdata","exitCode":0,"shell":"bash","uname":"Linux","sessionId":"71529b60-2e7b-4d5b-8dc1-6d0740b58e9e","cols":"211","lines":"56","home":"/home/simon","lang":"en_US.UTF-8","lcAll":"","login":"simon","pwd":"/home/simon","pwdAfter":"/home/simon","shellEnv":"/bin/bash","term":"xterm-256color","realPwd":"/home/simon","realPwdAfter":"/home/simon","pid":21224,"sessionPid":21224,"host":"simon-pc","hosttype":"x86_64","ostype":"linux-gnu","machtype":"x86_64-pc-linux-gnu","shlvl":1,"timezoneBefore":"+0200","timezoneAfter":"+0200","realtimeBefore":1569709838.4642656,"realtimeAfter":1569709838.4718792,"realtimeBeforeLocal":1569717038.4642656,"realtimeAfterLocal":1569717038.4718792,"realtimeDuration":0.007613658905029297,"realtimeSinceSessionStart":9.437154054641724,"realtimeSinceBoot":397011.02715405467,"gitDir":"","gitRealDir":"","gitOriginRemote":"","machineId":"c70365240bc647f09e2490722cc8186b","osReleaseId":"manjaro","osReleaseVersionId":"","osReleaseIdLike":"arch","osReleaseName":"Manjaro Linux","osReleasePrettyName":"Manjaro Linux","reshUuid":"","reshVersion":"1.1.3","reshRevision":"188d8b420493","sanitized":false}

@ -2,93 +2,50 @@ package recutil
import ( import (
"errors" "errors"
"net/url"
"strings"
"github.com/curusarn/resh/internal/record" "github.com/curusarn/resh/internal/record"
"github.com/mattn/go-shellwords" "github.com/curusarn/resh/internal/recordint"
giturls "github.com/whilp/git-urls"
) )
// NormalizeGitRemote helper // TODO: reintroduce validation
func NormalizeGitRemote(gitRemote string) string {
if strings.HasSuffix(gitRemote, ".git") {
gitRemote = gitRemote[:len(gitRemote)-4]
}
parsedURL, err := giturls.Parse(gitRemote)
if err != nil {
// TODO: log this error
return gitRemote
}
if parsedURL.User == nil || parsedURL.User.Username() == "" {
parsedURL.User = url.User("git")
}
// TODO: figure out what scheme we want
parsedURL.Scheme = "git+ssh"
return parsedURL.String()
}
// Validate returns error if the record is invalid // Validate returns error if the record is invalid
func Validate(r *record.V1) error { // func Validate(r *record.V1) error {
if r.CmdLine == "" { // if r.CmdLine == "" {
return errors.New("There is no CmdLine") // return errors.New("There is no CmdLine")
} // }
if r.RealtimeBefore == 0 || r.RealtimeAfter == 0 { // if r.Time == 0 {
return errors.New("There is no Time") // return errors.New("There is no Time")
} // }
if r.RealtimeBeforeLocal == 0 || r.RealtimeAfterLocal == 0 { // if r.RealPwd == "" {
return errors.New("There is no Local Time") // return errors.New("There is no Real Pwd")
} // }
if r.RealPwd == "" || r.RealPwdAfter == "" { // if r.Pwd == "" {
return errors.New("There is no Real Pwd") // return errors.New("There is no Pwd")
} // }
if r.Pwd == "" || r.PwdAfter == "" { // return nil
return errors.New("There is no Pwd") // }
}
return nil
}
// TODO: maybe more to a more appropriate place
// TODO: cleanup the interface - stop modifying the part1 and returning a ew record at the same time
// Merge two records (part1 - collect + part2 - postcollect) // Merge two records (part1 - collect + part2 - postcollect)
func Merge(r1 *record.V1, r2 *record.V1) error { func Merge(r1 *recordint.Collect, r2 *recordint.Collect) (record.V1, error) {
if r1.PartOne == false || r2.PartOne {
return errors.New("Expected part1 and part2 of the same record - usage: Merge(part1, part2)")
}
if r1.SessionID != r2.SessionID { if r1.SessionID != r2.SessionID {
return errors.New("Records to merge are not from the same sesion - r1:" + r1.SessionID + " r2:" + r2.SessionID) return record.V1{}, errors.New("Records to merge are not from the same sesion - r1:" + r1.SessionID + " r2:" + r2.SessionID)
}
if r1.CmdLine != r2.CmdLine {
return errors.New("Records to merge are not parts of the same records - r1:" + r1.CmdLine + " r2:" + r2.CmdLine)
} }
if r1.RecordID != r2.RecordID { if r1.Rec.RecordID != r2.Rec.RecordID {
return errors.New("Records to merge do not have the same ID - r1:" + r1.RecordID + " r2:" + r2.RecordID) return record.V1{}, errors.New("Records to merge do not have the same ID - r1:" + r1.Rec.RecordID + " r2:" + r2.Rec.RecordID)
} }
r1.ExitCode = r2.ExitCode
r1.Duration = r2.Duration
r1.PartsMerged = true r := recordint.Collect{
r1.PartOne = false SessionID: r1.SessionID,
return nil Shlvl: r1.Shlvl,
} SessionPID: r1.SessionPID,
// GetCommandAndFirstWord func Rec: r1.Rec,
func GetCommandAndFirstWord(cmdLine string) (string, string, error) {
args, err := shellwords.Parse(cmdLine)
if err != nil {
// Println("shellwords Error:", err, " (cmdLine: <", cmdLine, "> )")
return "", "", err
}
if len(args) == 0 {
return "", "", nil
}
i := 0
for true {
// commands in shell sometimes look like this `variable=something command argument otherArgument --option`
// to get the command we skip over tokens that contain '='
if strings.ContainsRune(args[i], '=') && len(args) > i+1 {
i++
continue
}
return args[i], args[0], nil
} }
return "ERROR", "ERROR", errors.New("failed to retrieve first word of command") r.Rec.ExitCode = r2.Rec.ExitCode
r.Rec.Duration = r2.Rec.Duration
r.Rec.PartOne = false
r.Rec.PartsNotMerged = false
return r.Rec, nil
} }

@ -7,7 +7,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/curusarn/resh/internal/records" "github.com/curusarn/resh/internal/recordint"
"golang.org/x/exp/utf8string" "golang.org/x/exp/utf8string"
) )
@ -18,7 +18,7 @@ const dots = "…"
type Item struct { type Item struct {
isRaw bool isRaw bool
realtimeBefore float64 time float64
// [host:]pwd // [host:]pwd
differentHost bool differentHost bool
@ -105,8 +105,8 @@ func (i Item) DrawStatusLine(compactRendering bool, printedLineLength, realLineL
if i.isRaw { if i.isRaw {
return splitStatusLineToLines(i.CmdLine, printedLineLength, realLineLength) return splitStatusLineToLines(i.CmdLine, printedLineLength, realLineLength)
} }
secs := int64(i.realtimeBefore) secs := int64(i.time)
nsecs := int64((i.realtimeBefore - float64(secs)) * 1e9) nsecs := int64((i.time - float64(secs)) * 1e9)
tm := time.Unix(secs, nsecs) tm := time.Unix(secs, nsecs)
const timeFormat = "2006-01-02 15:04:05" const timeFormat = "2006-01-02 15:04:05"
timeString := tm.Format(timeFormat) timeString := tm.Format(timeFormat)
@ -142,8 +142,8 @@ func (i Item) DrawItemColumns(compactRendering bool, debug bool) ItemColumns {
// DISPLAY // DISPLAY
// DISPLAY > date // DISPLAY > date
secs := int64(i.realtimeBefore) secs := int64(i.time)
nsecs := int64((i.realtimeBefore - float64(secs)) * 1e9) nsecs := int64((i.time - float64(secs)) * 1e9)
tm := time.Unix(secs, nsecs) tm := time.Unix(secs, nsecs)
var date string var date string
@ -314,7 +314,7 @@ func properMatch(str, term, padChar string) bool {
// NewItemFromRecordForQuery creates new item from record based on given query // NewItemFromRecordForQuery creates new item from record based on given query
// returns error if the query doesn't match the record // returns error if the query doesn't match the record
func NewItemFromRecordForQuery(record records.CliRecord, query Query, debug bool) (Item, error) { func NewItemFromRecordForQuery(record recordint.SearchApp, query Query, debug bool) (Item, error) {
// Use numbers that won't add up to same score for any number of query words // Use numbers that won't add up to same score for any number of query words
// query score weigth 1.51 // query score weigth 1.51
const hitScore = 1.517 // 1 * 1.51 const hitScore = 1.517 // 1 * 1.51
@ -411,10 +411,10 @@ func NewItemFromRecordForQuery(record records.CliRecord, query Query, debug bool
// if score <= 0 && !anyHit { // if score <= 0 && !anyHit {
// return Item{}, errors.New("no match for given record and query") // return Item{}, errors.New("no match for given record and query")
// } // }
score += record.RealtimeBefore * timeScoreCoef score += record.Time * timeScoreCoef
it := Item{ it := Item{
realtimeBefore: record.RealtimeBefore, time: record.Time,
differentHost: differentHost, differentHost: differentHost,
host: record.Host, host: record.Host,
@ -470,7 +470,7 @@ type RawItem struct {
// NewRawItemFromRecordForQuery creates new item from record based on given query // NewRawItemFromRecordForQuery creates new item from record based on given query
// returns error if the query doesn't match the record // returns error if the query doesn't match the record
func NewRawItemFromRecordForQuery(record records.CliRecord, terms []string, debug bool) (RawItem, error) { func NewRawItemFromRecordForQuery(record recordint.SearchApp, terms []string, debug bool) (RawItem, error) {
const hitScore = 1.0 const hitScore = 1.0
const hitScoreConsecutive = 0.01 const hitScoreConsecutive = 0.01
const properMatchScore = 0.3 const properMatchScore = 0.3
@ -489,7 +489,7 @@ func NewRawItemFromRecordForQuery(record records.CliRecord, terms []string, debu
cmd = strings.ReplaceAll(cmd, term, highlightMatch(term)) cmd = strings.ReplaceAll(cmd, term, highlightMatch(term))
} }
} }
score += record.RealtimeBefore * timeScoreCoef score += record.Time * timeScoreCoef
// KEY for deduplication // KEY for deduplication
key := record.CmdLine key := record.CmdLine

@ -3,20 +3,24 @@ package searchapp
import ( import (
"github.com/curusarn/resh/internal/histcli" "github.com/curusarn/resh/internal/histcli"
"github.com/curusarn/resh/internal/msg" "github.com/curusarn/resh/internal/msg"
"github.com/curusarn/resh/internal/records" "github.com/curusarn/resh/internal/recio"
"go.uber.org/zap" "go.uber.org/zap"
) )
// LoadHistoryFromFile ... // LoadHistoryFromFile ...
func LoadHistoryFromFile(sugar *zap.SugaredLogger, historyPath string, numLines int) msg.CliResponse { func LoadHistoryFromFile(sugar *zap.SugaredLogger, historyPath string, numLines int) msg.CliResponse {
recs := records.LoadFromFile(sugar, historyPath) rio := recio.New(sugar)
recs, _, err := rio.ReadFile(historyPath)
if err != nil {
sugar.Panicf("failed to read hisotry file: %w", err)
}
if numLines != 0 && numLines < len(recs) { if numLines != 0 && numLines < len(recs) {
recs = recs[:numLines] recs = recs[:numLines]
} }
cliRecords := histcli.New() cliRecords := histcli.New()
for i := len(recs) - 1; i >= 0; i-- { for i := len(recs) - 1; i >= 0; i-- {
rec := recs[i] rec := recs[i]
cliRecords.AddRecord(rec) cliRecords.AddRecord(&rec)
} }
return msg.CliResponse{CliRecords: cliRecords.List} return msg.CliResponse{Records: cliRecords.List}
} }

@ -4,7 +4,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/curusarn/resh/internal/records" "github.com/curusarn/resh/internal/recordint"
"github.com/mitchellh/go-ps" "github.com/mitchellh/go-ps"
"go.uber.org/zap" "go.uber.org/zap"
) )
@ -21,7 +21,7 @@ type sesswatch struct {
// Go runs the session watcher - watches sessions and sends // Go runs the session watcher - watches sessions and sends
func Go(sugar *zap.SugaredLogger, func Go(sugar *zap.SugaredLogger,
sessionsToWatch chan records.Record, sessionsToWatchRecords chan records.Record, sessionsToWatch chan recordint.SessionInit, sessionsToWatchRecords chan recordint.Collect,
sessionsToDrop []chan string, sleepSeconds uint) { sessionsToDrop []chan string, sleepSeconds uint) {
sw := sesswatch{ sw := sesswatch{
@ -33,17 +33,17 @@ func Go(sugar *zap.SugaredLogger,
go sw.waiter(sessionsToWatch, sessionsToWatchRecords) go sw.waiter(sessionsToWatch, sessionsToWatchRecords)
} }
func (s *sesswatch) waiter(sessionsToWatch chan records.Record, sessionsToWatchRecords chan records.Record) { func (s *sesswatch) waiter(sessionsToWatch chan recordint.SessionInit, sessionsToWatchRecords chan recordint.Collect) {
for { for {
func() { func() {
select { select {
case record := <-sessionsToWatch: case rec := <-sessionsToWatch:
// normal way to start watching a session // normal way to start watching a session
id := record.SessionID id := rec.SessionID
pid := record.SessionPID pid := rec.SessionPID
sugar := s.sugar.With( sugar := s.sugar.With(
"sessionID", record.SessionID, "sessionID", rec.SessionID,
"sessionPID", record.SessionPID, "sessionPID", rec.SessionPID,
) )
s.mutex.Lock() s.mutex.Lock()
defer s.mutex.Unlock() defer s.mutex.Unlock()
@ -52,13 +52,13 @@ func (s *sesswatch) waiter(sessionsToWatch chan records.Record, sessionsToWatchR
s.watchedSessions[id] = true s.watchedSessions[id] = true
go s.watcher(sugar, id, pid) go s.watcher(sugar, id, pid)
} }
case record := <-sessionsToWatchRecords: case rec := <-sessionsToWatchRecords:
// additional safety - watch sessions that were never properly initialized // additional safety - watch sessions that were never properly initialized
id := record.SessionID id := rec.SessionID
pid := record.SessionPID pid := rec.SessionPID
sugar := s.sugar.With( sugar := s.sugar.With(
"sessionID", record.SessionID, "sessionID", rec.SessionID,
"sessionPID", record.SessionPID, "sessionPID", rec.SessionPID,
) )
s.mutex.Lock() s.mutex.Lock()
defer s.mutex.Unlock() defer s.mutex.Unlock()

@ -8,30 +8,21 @@ __resh_preexec() {
# core # core
__RESH_COLLECT=1 __RESH_COLLECT=1
__RESH_CMDLINE="$1" # not local to preserve it for postcollect (useful as sanity check) __RESH_CMDLINE="$1" # not local to preserve it for postcollect (useful as sanity check)
local fpath_last_run="$__RESH_XDG_CACHE_HOME/collect_last_run_out.txt" __resh_collect --cmdLine "$__RESH_CMDLINE"
__resh_collect --cmdLine "$__RESH_CMDLINE" \
>| "$fpath_last_run" 2>&1 || echo "resh-collect ERROR: $(head -n 1 $fpath_last_run)"
} }
# used for collect and collect --recall # used for collect and collect --recall
__resh_collect() { __resh_collect() {
# posix # posix
local __RESH_COLS="$COLUMNS"
local __RESH_LANG="$LANG"
local __RESH_LC_ALL="$LC_ALL"
local __RESH_LINES="$LINES"
local __RESH_PWD="$PWD" local __RESH_PWD="$PWD"
# non-posix # non-posix
local __RESH_SHLVL="$SHLVL" local __RESH_SHLVL="$SHLVL"
local __RESH_GIT_CDUP; __RESH_GIT_CDUP="$(git rev-parse --show-cdup 2>/dev/null)"
local __RESH_GIT_CDUP_EXIT_CODE=$?
local __RESH_GIT_REMOTE; __RESH_GIT_REMOTE="$(git remote get-url origin 2>/dev/null)" local __RESH_GIT_REMOTE; __RESH_GIT_REMOTE="$(git remote get-url origin 2>/dev/null)"
local __RESH_GIT_REMOTE_EXIT_CODE=$? local __RESH_GIT_REMOTE_EXIT_CODE=$?
local __RESH_PID="$$" # current pid local __RESH_PID="$$" # current pid
# time # time
local __RESH_TZ_BEFORE; __RESH_TZ_BEFORE=$(date +%z)
# __RESH_RT_BEFORE="$EPOCHREALTIME" # __RESH_RT_BEFORE="$EPOCHREALTIME"
__RESH_RT_BEFORE=$(__resh_get_epochrealtime) __RESH_RT_BEFORE=$(__resh_get_epochrealtime)
@ -54,38 +45,16 @@ __resh_collect() {
resh-collect -requireVersion "$__RESH_VERSION" \ resh-collect -requireVersion "$__RESH_VERSION" \
-requireRevision "$__RESH_REVISION" \ -requireRevision "$__RESH_REVISION" \
-shell "$__RESH_SHELL" \ -shell "$__RESH_SHELL" \
-uname "$__RESH_UNAME" \ -sessionID "$__RESH_SESSION_ID" \
-sessionId "$__RESH_SESSION_ID" \ -recordID "$__RESH_RECORD_ID" \
-recordId "$__RESH_RECORD_ID" \
-cols "$__RESH_COLS" \
-home "$__RESH_HOME" \ -home "$__RESH_HOME" \
-lang "$__RESH_LANG" \ -logname "$__RESH_LOGIN" \
-lcAll "$__RESH_LC_ALL" \
-lines "$__RESH_LINES" \
-login "$__RESH_LOGIN" \
-pwd "$__RESH_PWD" \ -pwd "$__RESH_PWD" \
-shellEnv "$__RESH_SHELL_ENV" \ -sessionPID "$__RESH_SESSION_PID" \
-term "$__RESH_TERM" \ -hostname "$__RESH_HOST" \
-pid "$__RESH_PID" \
-sessionPid "$__RESH_SESSION_PID" \
-host "$__RESH_HOST" \
-hosttype "$__RESH_HOSTTYPE" \
-ostype "$__RESH_OSTYPE" \
-machtype "$__RESH_MACHTYPE" \
-shlvl "$__RESH_SHLVL" \ -shlvl "$__RESH_SHLVL" \
-gitCdup "$__RESH_GIT_CDUP" \
-gitCdupExitCode "$__RESH_GIT_CDUP_EXIT_CODE" \
-gitRemote "$__RESH_GIT_REMOTE" \ -gitRemote "$__RESH_GIT_REMOTE" \
-gitRemoteExitCode "$__RESH_GIT_REMOTE_EXIT_CODE" \ -time "$__RESH_RT_BEFORE" \
-realtimeBefore "$__RESH_RT_BEFORE" \
-realtimeSession "$__RESH_RT_SESSION" \
-realtimeSessSinceBoot "$__RESH_RT_SESS_SINCE_BOOT" \
-timezoneBefore "$__RESH_TZ_BEFORE" \
-osReleaseId "$__RESH_OS_RELEASE_ID" \
-osReleaseVersionId "$__RESH_OS_RELEASE_VERSION_ID" \
-osReleaseIdLike "$__RESH_OS_RELEASE_ID_LIKE" \
-osReleaseName "$__RESH_OS_RELEASE_NAME" \
-osReleasePrettyName "$__RESH_OS_RELEASE_PRETTY_NAME" \
"$@" "$@"
return $? return $?
fi fi
@ -95,20 +64,8 @@ __resh_collect() {
__resh_precmd() { __resh_precmd() {
local __RESH_EXIT_CODE=$? local __RESH_EXIT_CODE=$?
local __RESH_RT_AFTER local __RESH_RT_AFTER
local __RESH_TZ_AFTER
local __RESH_PWD_AFTER
local __RESH_GIT_CDUP_AFTER
local __RESH_GIT_CDUP_EXIT_CODE_AFTER
local __RESH_GIT_REMOTE_AFTER
local __RESH_GIT_REMOTE_EXIT_CODE_AFTER
local __RESH_SHLVL="$SHLVL" local __RESH_SHLVL="$SHLVL"
__RESH_RT_AFTER=$(__resh_get_epochrealtime) __RESH_RT_AFTER=$(__resh_get_epochrealtime)
__RESH_TZ_AFTER=$(date +%z)
__RESH_PWD_AFTER="$PWD"
__RESH_GIT_CDUP_AFTER="$(git rev-parse --show-cdup 2>/dev/null)"
__RESH_GIT_CDUP_EXIT_CODE_AFTER=$?
__RESH_GIT_REMOTE_AFTER="$(git remote get-url origin 2>/dev/null)"
__RESH_GIT_REMOTE_EXIT_CODE_AFTER=$?
if [ -n "${__RESH_COLLECT}" ]; then if [ -n "${__RESH_COLLECT}" ]; then
if [ "$__RESH_VERSION" != "$(resh-postcollect -version)" ]; then if [ "$__RESH_VERSION" != "$(resh-postcollect -version)" ]; then
# shellcheck source=shellrc.sh # shellcheck source=shellrc.sh
@ -126,24 +83,14 @@ __resh_precmd() {
fi fi
fi fi
if [ "$__RESH_VERSION" = "$(resh-postcollect -version)" ] && [ "$__RESH_REVISION" = "$(resh-postcollect -revision)" ]; then if [ "$__RESH_VERSION" = "$(resh-postcollect -version)" ] && [ "$__RESH_REVISION" = "$(resh-postcollect -revision)" ]; then
local fpath_last_run="$__RESH_XDG_CACHE_HOME/postcollect_last_run_out.txt"
resh-postcollect -requireVersion "$__RESH_VERSION" \ resh-postcollect -requireVersion "$__RESH_VERSION" \
-requireRevision "$__RESH_REVISION" \ -requireRevision "$__RESH_REVISION" \
-cmdLine "$__RESH_CMDLINE" \ -timeBefore "$__RESH_RT_BEFORE" \
-realtimeBefore "$__RESH_RT_BEFORE" \
-exitCode "$__RESH_EXIT_CODE" \ -exitCode "$__RESH_EXIT_CODE" \
-sessionId "$__RESH_SESSION_ID" \ -sessionID "$__RESH_SESSION_ID" \
-recordId "$__RESH_RECORD_ID" \ -recordID "$__RESH_RECORD_ID" \
-shell "$__RESH_SHELL" \
-shlvl "$__RESH_SHLVL" \ -shlvl "$__RESH_SHLVL" \
-pwdAfter "$__RESH_PWD_AFTER" \ -timeAfter "$__RESH_RT_AFTER"
-gitCdupAfter "$__RESH_GIT_CDUP_AFTER" \
-gitCdupExitCodeAfter "$__RESH_GIT_CDUP_EXIT_CODE_AFTER" \
-gitRemoteAfter "$__RESH_GIT_REMOTE_AFTER" \
-gitRemoteExitCodeAfter "$__RESH_GIT_REMOTE_EXIT_CODE_AFTER" \
-realtimeAfter "$__RESH_RT_AFTER" \
-timezoneAfter "$__RESH_TZ_AFTER" \
>| "$fpath_last_run" 2>&1 || echo "resh-postcollect ERROR: $(head -n 1 $fpath_last_run)"
fi fi
__resh_reset_variables __resh_reset_variables
fi fi

@ -135,7 +135,7 @@ bin/resh-control completion zsh > ~/.resh/zsh_completion.d/_reshctl
echo "Copying more files ..." echo "Copying more files ..."
cp -f scripts/uuid.sh ~/.resh/bin/resh-uuid cp -f scripts/uuid.sh ~/.resh/bin/resh-uuid
cp -f bin/resh-{daemon,control,collect,postcollect,session-init,config} ~/.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-config-setup ./bin/resh-config-setup
@ -179,7 +179,8 @@ else
pkill -SIGTERM "resh-daemon" || true pkill -SIGTERM "resh-daemon" || true
fi fi
# daemon uses xdg path variables # daemon uses xdg path variables
__resh_set_xdg_home_paths # FIXME: this does not exist anymore
#__resh_set_xdg_home_paths
__resh_run_daemon __resh_run_daemon

@ -74,8 +74,6 @@ resh() {
elif [ $status_code = 130 ]; then elif [ $status_code = 130 ]; then
true true
else else
local fpath_last_run="$__RESH_XDG_CACHE_HOME/cli_last_run_out.txt" printf "%s" "$buffer" >&2
echo "$buffer" >| "$fpath_last_run"
echo "resh-cli failed - check '$fpath_last_run' and '~/.resh/cli.log'"
fi fi
} }

@ -55,7 +55,8 @@ export __RESH_VERSION=$(resh-collect -version)
# shellcheck disable=2155 # shellcheck disable=2155
export __RESH_REVISION=$(resh-collect -revision) export __RESH_REVISION=$(resh-collect -revision)
__resh_set_xdg_home_paths # FIXME: this does not exist anymore
# __resh_set_xdg_home_paths
__resh_run_daemon __resh_run_daemon

@ -47,19 +47,21 @@ __resh_get_epochrealtime() {
fi fi
} }
# FIXME: figure out if stdout/stderr should be discarded
__resh_run_daemon() { __resh_run_daemon() {
if [ -n "${ZSH_VERSION-}" ]; then if [ -n "${ZSH_VERSION-}" ]; then
setopt LOCAL_OPTIONS NO_NOTIFY NO_MONITOR setopt LOCAL_OPTIONS NO_NOTIFY NO_MONITOR
fi fi
local fpath_last_run="$__RESH_XDG_CACHE_HOME/daemon_last_run_out.txt"
if [ "$(uname)" = Darwin ]; then if [ "$(uname)" = Darwin ]; then
# hotfix # hotfix
gnohup resh-daemon >| "$fpath_last_run" 2>&1 & disown # gnohup resh-daemon 2>&1 & disown
gnohup resh-daemon & disown
else else
# TODO: switch to nohup for consistency once you confirm that daemon is # TODO: switch to nohup for consistency once you confirm that daemon is
# not getting killed anymore on macOS # not getting killed anymore on macOS
# nohup resh-daemon >| "$fpath_last_run" 2>&1 & disown nohup resh-daemon & disown
setsid resh-daemon >| "$fpath_last_run" 2>&1 & disown #nohup resh-daemon 2>&1 & disown
#setsid resh-daemon 2>&1 & disown
fi fi
} }
@ -71,7 +73,7 @@ __resh_bash_completion_init() {
. ~/.resh/bash_completion.d/_reshctl . ~/.resh/bash_completion.d/_reshctl
} }
// TODO: redo this # TODO: redo this
__resh_zsh_completion_init() { __resh_zsh_completion_init() {
# NOTE: this is hacky - each completion needs to be added individually # NOTE: this is hacky - each completion needs to be added individually
# TODO: fix later # TODO: fix later
@ -131,32 +133,8 @@ __resh_session_init() {
if [ "$__RESH_VERSION" = "$(resh-session-init -version)" ] && [ "$__RESH_REVISION" = "$(resh-session-init -revision)" ]; then if [ "$__RESH_VERSION" = "$(resh-session-init -version)" ] && [ "$__RESH_REVISION" = "$(resh-session-init -revision)" ]; then
resh-session-init -requireVersion "$__RESH_VERSION" \ resh-session-init -requireVersion "$__RESH_VERSION" \
-requireRevision "$__RESH_REVISION" \ -requireRevision "$__RESH_REVISION" \
-shell "$__RESH_SHELL" \
-uname "$__RESH_UNAME" \
-sessionId "$__RESH_SESSION_ID" \ -sessionId "$__RESH_SESSION_ID" \
-cols "$__RESH_COLS" \ -sessionPid "$__RESH_SESSION_PID"
-home "$__RESH_HOME" \
-lang "$__RESH_LANG" \
-lcAll "$__RESH_LC_ALL" \
-lines "$__RESH_LINES" \
-login "$__RESH_LOGIN" \
-shellEnv "$__RESH_SHELL_ENV" \
-term "$__RESH_TERM" \
-pid "$__RESH_PID" \
-sessionPid "$__RESH_SESSION_PID" \
-host "$__RESH_HOST" \
-hosttype "$__RESH_HOSTTYPE" \
-ostype "$__RESH_OSTYPE" \
-machtype "$__RESH_MACHTYPE" \
-shlvl "$__RESH_SHLVL" \
-realtimeBefore "$__RESH_RT_BEFORE" \
-realtimeSession "$__RESH_RT_SESSION" \
-realtimeSessSinceBoot "$__RESH_RT_SESS_SINCE_BOOT" \
-timezoneBefore "$__RESH_TZ_BEFORE" \
-osReleaseId "$__RESH_OS_RELEASE_ID" \
-osReleaseVersionId "$__RESH_OS_RELEASE_VERSION_ID" \
-osReleaseIdLike "$__RESH_OS_RELEASE_ID_LIKE" \
-osReleaseName "$__RESH_OS_RELEASE_NAME" \
-osReleasePrettyName "$__RESH_OS_RELEASE_PRETTY_NAME"
fi fi
} }

@ -16,8 +16,6 @@ __resh_widget_control_R() {
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" --host "$__RESH_HOST" --pwd "$PWD" --gitOriginRemote "$git_remote" --query "$BUFFER")
status_code=$? status_code=$?
local fpath_last_run="$__RESH_XDG_CACHE_HOME/cli_last_run_out.txt"
touch "$fpath_last_run"
if [ $status_code = 111 ]; then if [ $status_code = 111 ]; then
# execute # execute
if [ -n "${ZSH_VERSION-}" ]; then if [ -n "${ZSH_VERSION-}" ]; then
@ -35,8 +33,8 @@ __resh_widget_control_R() {
bind -x '"\u[32~": __resh_nop' bind -x '"\u[32~": __resh_nop'
fi fi
else else
echo "$BUFFER" >| "$fpath_last_run" echo "RESH SEARCH APP failed"
echo "# RESH SEARCH APP failed - sorry for the inconvinience - check '$fpath_last_run' and '~/.resh/cli.log'" printf "%s" "$buffer" >&2
BUFFER="$PREVBUFFER" BUFFER="$PREVBUFFER"
fi fi
CURSOR=${#BUFFER} CURSOR=${#BUFFER}

Loading…
Cancel
Save