mirror of
https://github.com/ddworken/hishtory.git
synced 2025-06-23 05:22:02 +02:00
working tracking for start time
This commit is contained in:
parent
11962b26c9
commit
3cc9118a69
@ -1,5 +0,0 @@
|
||||
Installation:
|
||||
|
||||
```
|
||||
export PROMPT_COMMAND='~/.hishtory-client saveHistoryEntry $? "`history 1`"'
|
||||
```
|
@ -22,7 +22,6 @@ func main() {
|
||||
shared.CheckFatalError(shared.Setup(os.Args))
|
||||
case "install":
|
||||
shared.CheckFatalError(shared.Install())
|
||||
shared.CheckFatalError(shared.Setup(os.Args))
|
||||
case "enable":
|
||||
shared.CheckFatalError(shared.Enable())
|
||||
case "disable":
|
||||
|
@ -25,7 +25,6 @@ func main() {
|
||||
case "init":
|
||||
shared.CheckFatalError(shared.Setup(os.Args))
|
||||
case "install":
|
||||
shared.CheckFatalError(shared.Setup(os.Args))
|
||||
shared.CheckFatalError(shared.Install())
|
||||
case "enable":
|
||||
shared.CheckFatalError(shared.Enable())
|
||||
|
@ -17,7 +17,7 @@ func TestSubmitThenQuery(t *testing.T) {
|
||||
shared.Check(t, shared.Setup([]string{}))
|
||||
|
||||
// Submit an entry
|
||||
entry, err := shared.BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 ls / "})
|
||||
entry, err := shared.BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 ls / ", "1641774958326745663"})
|
||||
shared.Check(t, err)
|
||||
reqBody, err := json.Marshal(entry)
|
||||
shared.Check(t, err)
|
||||
@ -63,7 +63,7 @@ func TestNoUserSecretGivesNoResults(t *testing.T) {
|
||||
shared.Check(t, shared.Setup([]string{}))
|
||||
|
||||
// Submit an entry
|
||||
entry, err := shared.BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 ls / "})
|
||||
entry, err := shared.BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 ls / ", "1641774958326745663"})
|
||||
shared.Check(t, err)
|
||||
reqBody, err := json.Marshal(entry)
|
||||
shared.Check(t, err)
|
||||
@ -91,14 +91,14 @@ func TestSearchQuery(t *testing.T) {
|
||||
shared.Check(t, shared.Setup([]string{}))
|
||||
|
||||
// Submit an entry that we'll match
|
||||
entry, err := shared.BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 ls /bar "})
|
||||
entry, err := shared.BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 ls /bar ", "1641774958326745663"})
|
||||
shared.Check(t, err)
|
||||
reqBody, err := json.Marshal(entry)
|
||||
shared.Check(t, err)
|
||||
submitReq := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(reqBody))
|
||||
apiSubmitHandler(nil, submitReq)
|
||||
// Submit an entry that we won't match
|
||||
entry, err = shared.BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 ls /foo "})
|
||||
entry, err = shared.BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 ls /foo ", "1641774958326745663"})
|
||||
shared.Check(t, err)
|
||||
reqBody, err = json.Marshal(entry)
|
||||
shared.Check(t, err)
|
||||
|
@ -1,6 +1,8 @@
|
||||
package shared
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
@ -24,6 +26,11 @@ const (
|
||||
CONFIG_PATH = ".hishtory.config"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed config.sh
|
||||
CONFIG_SH_CONTENTS string
|
||||
)
|
||||
|
||||
func getCwd() (string, error) {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
@ -63,7 +70,12 @@ func BuildHistoryEntry(args []string) (*HistoryEntry, error) {
|
||||
}
|
||||
entry.CurrentWorkingDirectory = cwd
|
||||
|
||||
// TODO(ddworken): start time
|
||||
// start time
|
||||
nanos, err := strconv.ParseInt(args[4], 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse start time %s as int: %v", args[4], err)
|
||||
}
|
||||
entry.StartTime = time.Unix(0, nanos)
|
||||
|
||||
// end time
|
||||
entry.EndTime = time.Now()
|
||||
@ -119,17 +131,19 @@ func Setup(args []string) error {
|
||||
|
||||
func DisplayResults(results []*HistoryEntry, displayHostname bool) {
|
||||
headerFmt := color.New(color.FgGreen, color.Underline).SprintfFunc()
|
||||
tbl := table.New("CWD", "Timestamp", "Exit Code", "Command")
|
||||
tbl := table.New("CWD", "Timestamp", "Runtime", "Exit Code", "Command")
|
||||
if displayHostname {
|
||||
tbl = table.New("Hostname", "CWD", "Timestamp", "Exit Code", "Command")
|
||||
tbl = table.New("Hostname", "CWD", "Timestamp", "Runtime", "Exit Code", "Command")
|
||||
}
|
||||
tbl.WithHeaderFormatter(headerFmt)
|
||||
|
||||
for _, result := range results {
|
||||
timestamp := result.StartTime.Format("Jan 2 2006 15:04:05 MST")
|
||||
duration := result.EndTime.Sub(result.StartTime).Round(time.Millisecond).String()
|
||||
if displayHostname {
|
||||
tbl.AddRow(result.Hostname, result.CurrentWorkingDirectory, result.EndTime.Format("Jan 2 2006 15:04:05 MST"), result.ExitCode, result.Command)
|
||||
tbl.AddRow(result.Hostname, result.CurrentWorkingDirectory, timestamp, duration, result.ExitCode, result.Command)
|
||||
} else {
|
||||
tbl.AddRow(result.CurrentWorkingDirectory, result.EndTime.Format("Jan 2 2006 15:04:05 MST"), result.ExitCode, result.Command)
|
||||
tbl.AddRow(result.CurrentWorkingDirectory, timestamp, duration, result.ExitCode, result.Command)
|
||||
}
|
||||
}
|
||||
|
||||
@ -206,10 +220,6 @@ func CheckFatalError(err error) {
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
PROMPT_COMMAND = "export PROMPT_COMMAND='%s saveHistoryEntry $? \"`history 1`\"'"
|
||||
)
|
||||
|
||||
func Install() error {
|
||||
homedir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
@ -219,24 +229,41 @@ func Install() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return configureBashrc(homedir, path)
|
||||
err = configureBashrc(homedir, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = GetConfig()
|
||||
if err != nil {
|
||||
// No config, so set up a new installation
|
||||
return Setup(os.Args)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func configureBashrc(homedir, binaryPath string) error {
|
||||
promptCommand := fmt.Sprintf(PROMPT_COMMAND, binaryPath)
|
||||
// Check if we need to configure the bashrc
|
||||
bashrc, err := ioutil.ReadFile(path.Join(homedir, ".bashrc"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read bashrc: %v", err)
|
||||
}
|
||||
if strings.Contains(string(bashrc), promptCommand) {
|
||||
if strings.Contains(string(bashrc), "# Hishtory Config:") {
|
||||
return nil
|
||||
}
|
||||
// Create the file we're going to source in our bashrc
|
||||
bashConfigPath := path.Join(filepath.Dir(binaryPath), "config.sh")
|
||||
err = ioutil.WriteFile(bashConfigPath, []byte(CONFIG_SH_CONTENTS), 0644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write config.sh file: %v", err)
|
||||
}
|
||||
|
||||
// Add to bashrc
|
||||
f, err := os.OpenFile(path.Join(homedir, ".bashrc"), os.O_APPEND|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to append to bashrc: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
_, err = f.WriteString(string(bashrc) + "\n# Hishtory Config:\nexport PATH=\"$PATH:" + filepath.Dir(binaryPath) + "\"\n" + promptCommand + "\n")
|
||||
_, err = f.WriteString("\n# Hishtory Config:\nexport PATH=\"$PATH:" + filepath.Dir(binaryPath) + "\"\nsource " + bashConfigPath + "\n")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to append to bashrc: %v", err)
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestSetup(t *testing.T) {
|
||||
@ -28,7 +29,7 @@ func TestSetup(t *testing.T) {
|
||||
func TestBuildHistoryEntry(t *testing.T) {
|
||||
defer BackupAndRestore(t)
|
||||
Check(t, Setup([]string{}))
|
||||
entry, err := BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 ls / "})
|
||||
entry, err := BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 ls / ", "1641774958326745663"})
|
||||
Check(t, err)
|
||||
if entry.UserSecret == "" || len(entry.UserSecret) < 10 || strings.TrimSpace(entry.UserSecret) != entry.UserSecret {
|
||||
t.Fatalf("history entry has unexpected user secret: %v", entry.UserSecret)
|
||||
@ -45,6 +46,9 @@ func TestBuildHistoryEntry(t *testing.T) {
|
||||
if entry.Command != "ls /" {
|
||||
t.Fatalf("history entry has unexpected command: %v", entry.Command)
|
||||
}
|
||||
if entry.StartTime.Format(time.RFC3339) != "2022-01-09T16:35:58-08:00" {
|
||||
t.Fatalf("history entry has incorrect start time: %v", entry.StartTime.Format(time.RFC3339))
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetUserSecret(t *testing.T) {
|
||||
|
27
shared/config.sh
Normal file
27
shared/config.sh
Normal file
@ -0,0 +1,27 @@
|
||||
# This script should be sourced inside of .bashrc to integrate bash with hishtory
|
||||
|
||||
# Implementation of PreCommand and PostCommand based on https://jichu4n.com/posts/debug-trap-and-prompt_command-in-bash/
|
||||
function PreCommand() {
|
||||
if [ -z "$HISHTORY_AT_PROMPT" ]; then
|
||||
return
|
||||
fi
|
||||
unset HISHTORY_AT_PROMPT
|
||||
|
||||
# Run before every command
|
||||
HISHTORY_START_TIME=`date +%s%N`
|
||||
}
|
||||
trap "PreCommand" DEBUG
|
||||
|
||||
HISHTORY_FIRST_PROMPT=1
|
||||
function PostCommand() {
|
||||
HISHTORY_AT_PROMPT=1
|
||||
|
||||
if [ -n "$HISHTORY_FIRST_PROMPT" ]; then
|
||||
unset HISHTORY_FIRST_PROMPT
|
||||
return
|
||||
fi
|
||||
|
||||
# Run after every prompt
|
||||
hishtory saveHistoryEntry $? "`history 1`" $HISHTORY_START_TIME
|
||||
}
|
||||
PROMPT_COMMAND="PostCommand"
|
@ -2,7 +2,6 @@ package shared
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
@ -28,7 +27,6 @@ const (
|
||||
)
|
||||
|
||||
func Persist(entry HistoryEntry) error {
|
||||
log.Printf("Saving %#v to the DB\n", entry)
|
||||
db, err := OpenDB()
|
||||
if err != nil {
|
||||
return err
|
||||
@ -46,7 +44,7 @@ func OpenDB() (*gorm.DB, error) {
|
||||
}
|
||||
db, err := gorm.Open(sqlite.Open(path.Join(homedir, DB_PATH)), &gorm.Config{})
|
||||
if err != nil {
|
||||
panic("failed to connect database")
|
||||
return nil, fmt.Errorf("failed to connect to the DB: %v", err)
|
||||
}
|
||||
db.AutoMigrate(&HistoryEntry{})
|
||||
return db, nil
|
||||
@ -65,6 +63,8 @@ func Search(db *gorm.DB, userSecret, query string, limit int) ([]*HistoryEntry,
|
||||
val := splitToken[1]
|
||||
// tx = tx.Where()
|
||||
panic("TODO(ddworken): Use " + field + val)
|
||||
} else if strings.HasPrefix(token, "-") {
|
||||
panic("TODO(ddworken): Implement -foo as filtering out foo")
|
||||
} else {
|
||||
wildcardedToken := "%" + token + "%"
|
||||
tx = tx.Where("(command LIKE ? OR hostname LIKE ? OR current_working_directory LIKE ?)", wildcardedToken, wildcardedToken, wildcardedToken)
|
||||
|
@ -7,7 +7,7 @@ import (
|
||||
func TestPersist(t *testing.T) {
|
||||
defer BackupAndRestore(t)
|
||||
Check(t, Setup([]string{}))
|
||||
entry, err := BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 ls / "})
|
||||
entry, err := BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 ls / ", "1641774958326745663"})
|
||||
Check(t, err)
|
||||
Check(t, Persist(*entry))
|
||||
|
||||
@ -30,13 +30,13 @@ func TestSearch(t *testing.T) {
|
||||
Check(t, Setup([]string{}))
|
||||
|
||||
// Insert data
|
||||
entry1, err := BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 ls / "})
|
||||
entry1, err := BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 ls / ", "1641774958326745663"})
|
||||
Check(t, err)
|
||||
Check(t, Persist(*entry1))
|
||||
entry2, err := BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 ls /foo "})
|
||||
entry2, err := BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 ls /foo ", "1641774958326745663"})
|
||||
Check(t, err)
|
||||
Check(t, Persist(*entry2))
|
||||
entry3, err := BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 echo hi "})
|
||||
entry3, err := BuildHistoryEntry([]string{"unused", "saveHistoryEntry", "120", " 123 echo hi ", "1641774958326745663"})
|
||||
Check(t, err)
|
||||
Check(t, Persist(*entry3))
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user