from origin repo gh:ddworken/hishtory, commit 480630e9181167b51554f4407db55717d9b7e4dd
This commit is contained in:
34
client/lib/config.fish
Normal file
34
client/lib/config.fish
Normal file
@@ -0,0 +1,34 @@
|
||||
function _hishtory_post_exec --on-event fish_postexec
|
||||
# Runs after <ENTER>, but before the command is executed
|
||||
set --global _hishtory_command $argv
|
||||
set --global _hishtory_start_time (date +%s)
|
||||
end
|
||||
|
||||
set --global _hishtory_first_prompt 1
|
||||
|
||||
function __hishtory_on_prompt --on-event fish_prompt
|
||||
# Runs after the command is executed in order to render the prompt
|
||||
# $? contains the exit code
|
||||
set _hishtory_exit_code $status
|
||||
if [ -n "$_hishtory_first_prompt" ]
|
||||
set --global -e _hishtory_first_prompt
|
||||
else if [ -n "$_hishtory_command" ]
|
||||
hishtory saveHistoryEntry fish $_hishtory_exit_code "$_hishtory_command" $_hishtory_start_time & # Background Run
|
||||
# hishtory saveHistoryEntry fish $_hishtory_exit_code "$_hishtory_command" $_hishtory_start_time # Foreground Run
|
||||
set --global -e _hishtory_command # Unset _hishtory_command so we don't double-save entries when fish_prompt is invoked but fish_postexec isn't
|
||||
end
|
||||
end
|
||||
|
||||
function __hishtory_on_control_r
|
||||
set -l tmp (mktemp -t fish.XXXXXX)
|
||||
set -x init_query (commandline -b)
|
||||
HISHTORY_TERM_INTEGRATION=1 hishtory tquery $init_query > $tmp
|
||||
set -l res $status
|
||||
commandline -f repaint
|
||||
if [ -s $tmp ]
|
||||
commandline -r (cat $tmp)
|
||||
end
|
||||
rm -f $tmp
|
||||
end
|
||||
|
||||
[ (hishtory config-get enable-control-r) = true ] && bind \cr __hishtory_on_control_r
|
46
client/lib/config.sh
Normal file
46
client/lib/config.sh
Normal file
@@ -0,0 +1,46 @@
|
||||
# This script should be sourced inside of .bashrc to integrate bash with hishtory
|
||||
|
||||
# Include guard. This file is sourced in multiple places, but we want it to only execute once.
|
||||
# This trick is from https://stackoverflow.com/questions/7518584/is-there-any-mechanism-in-shell-script-alike-include-guard-in-c
|
||||
if [ -n "$__hishtory_bash_config_sourced" ]; then return; fi
|
||||
__hishtory_bash_config_sourced=`date`
|
||||
|
||||
# Implementation of running before/after every command based on https://jichu4n.com/posts/debug-trap-and-prompt_command-in-bash/
|
||||
function __hishtory_precommand() {
|
||||
if [ -z "$HISHTORY_AT_PROMPT" ]; then
|
||||
return
|
||||
fi
|
||||
unset HISHTORY_AT_PROMPT
|
||||
|
||||
# Run before every command
|
||||
HISHTORY_START_TIME=`date +%s`
|
||||
}
|
||||
trap "__hishtory_precommand" DEBUG
|
||||
|
||||
HISHTORY_FIRST_PROMPT=1
|
||||
function __hishtory_postcommand() {
|
||||
EXIT_CODE=$?
|
||||
HISHTORY_AT_PROMPT=1
|
||||
|
||||
if [ -n "$HISHTORY_FIRST_PROMPT" ]; then
|
||||
unset HISHTORY_FIRST_PROMPT
|
||||
return
|
||||
fi
|
||||
|
||||
# Run after every prompt
|
||||
(hishtory saveHistoryEntry bash $EXIT_CODE "`history 1`" $HISHTORY_START_TIME &) # Background Run
|
||||
# hishtory saveHistoryEntry bash $EXIT_CODE "`history 1`" $HISHTORY_START_TIME # Foreground Run
|
||||
}
|
||||
PROMPT_COMMAND="__hishtory_postcommand; $PROMPT_COMMAND"
|
||||
export HISTTIMEFORMAT=$HISTTIMEFORMAT
|
||||
|
||||
__history_control_r() {
|
||||
READLINE_LINE=$(HISHTORY_TERM_INTEGRATION=1 hishtory tquery "$READLINE_LINE" | tr -d '\n')
|
||||
READLINE_POINT=0x7FFFFFFF
|
||||
}
|
||||
|
||||
__hishtory_bind_control_r() {
|
||||
bind -x '"\C-r": __history_control_r'
|
||||
}
|
||||
|
||||
[ "$(hishtory config-get enable-control-r)" = true ] && __hishtory_bind_control_r
|
37
client/lib/config.zsh
Normal file
37
client/lib/config.zsh
Normal file
@@ -0,0 +1,37 @@
|
||||
autoload -U add-zsh-hook
|
||||
add-zsh-hook zshaddhistory _hishtory_add
|
||||
add-zsh-hook precmd _hishtory_precmd
|
||||
|
||||
_hishtory_first_prompt=1
|
||||
|
||||
function _hishtory_add() {
|
||||
# Runs after <ENTER>, but before the command is executed
|
||||
# $1 contains the command that was run
|
||||
_hishtory_command=$1
|
||||
_hishtory_start_time=`date +%s`
|
||||
}
|
||||
|
||||
function _hishtory_precmd() {
|
||||
# Runs after the command is executed in order to render the prompt
|
||||
# $? contains the exit code
|
||||
_hishtory_exit_code=$?
|
||||
if [ -n "$_hishtory_first_prompt" ]; then
|
||||
unset _hishtory_first_prompt
|
||||
return
|
||||
fi
|
||||
(hishtory saveHistoryEntry zsh $_hishtory_exit_code "$_hishtory_command" $_hishtory_start_time &) # Background Run
|
||||
# hishtory saveHistoryEntry zsh $_hishtory_exit_code "$_hishtory_command" $_hishtory_start_time # Foreground Run
|
||||
}
|
||||
|
||||
_hishtory_widget() {
|
||||
BUFFER=$(HISHTORY_TERM_INTEGRATION=1 hishtory tquery $BUFFER | tr -d '\n')
|
||||
CURSOR=${#BUFFER}
|
||||
zle reset-prompt
|
||||
}
|
||||
|
||||
_hishtory_bind_control_r() {
|
||||
zle -N _hishtory_widget
|
||||
bindkey '^R' _hishtory_widget
|
||||
}
|
||||
|
||||
[ "$(hishtory config-get enable-control-r)" = true ] && _hishtory_bind_control_r
|
6
client/lib/goldens/TestFish-table
Normal file
6
client/lib/goldens/TestFish-table
Normal file
@@ -0,0 +1,6 @@
|
||||
CWD Hostname Exit Code Command
|
||||
/ ghaction-runner-hostname 0 hishtory config-set displayed-columns CWD Hostname 'Exit Code' Command
|
||||
/ ghaction-runner-hostname 0 ls /tmp/ &
|
||||
/ ghaction-runner-hostname 0 echo "foo"
|
||||
/ ghaction-runner-hostname 0 echo bar
|
||||
/ ghaction-runner-hostname 0 echo foo
|
3
client/lib/goldens/TestTimestampFormat-query
Normal file
3
client/lib/goldens/TestTimestampFormat-query
Normal file
@@ -0,0 +1,3 @@
|
||||
Hostname CWD Timestamp Runtime Exit Code Command
|
||||
localhost ~/foo/ 2022/Apr/16 01:03 24s 3 table_cmd2
|
||||
localhost /tmp/ 2022/Apr/16 01:03 4s 2 table_cmd1 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
30
client/lib/goldens/TestTimestampFormat-tquery
Normal file
30
client/lib/goldens/TestTimestampFormat-tquery
Normal file
@@ -0,0 +1,30 @@
|
||||
-pipefail -tablesizing
|
||||
|
||||
|
||||
|
||||
Search Query: > -pipefail -tablesizing
|
||||
|
||||
┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Hostname CWD Timestamp Runtime Exit Code Command │
|
||||
│───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
||||
│ localhost ~/foo/ 2022/Apr/16 01:03 24s 3 table_cmd2 │
|
||||
│ localhost /tmp/ 2022/Apr/16 01:03 4s 2 table_cmd1 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa… │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
└───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
30
client/lib/goldens/TestTimestampFormat-tquery-isAction
Normal file
30
client/lib/goldens/TestTimestampFormat-tquery-isAction
Normal file
@@ -0,0 +1,30 @@
|
||||
-pipefail -tablesizing
|
||||
|
||||
|
||||
|
||||
Search Query: > -pipefail -tablesizing
|
||||
|
||||
┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Hostname CWD Timestamp Runtime Exit Code Command │
|
||||
│───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
||||
│ localhost ~/foo/ 2022/Apr/16 01:03 24s 3 table_cmd2 │
|
||||
│ localhost /tmp/ 2022/Apr/16 01:03 4s 2 table_cmd1 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa… │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
└───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
26
client/lib/goldens/TestTui-Initial
Normal file
26
client/lib/goldens/TestTui-Initial
Normal file
@@ -0,0 +1,26 @@
|
||||
Search Query: > ls
|
||||
|
||||
┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Hostname CWD Timestamp Runtime Exit Code Command │
|
||||
│────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
||||
│ localhost /tmp/ Oct 17 2022 21:43:21 PDT 3s 2 echo 'aaaaaa bbbb' │
|
||||
│ localhost /tmp/ Oct 17 2022 21:43:16 PDT 3s 2 ls ~/ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
└────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
26
client/lib/goldens/TestTui-Search
Normal file
26
client/lib/goldens/TestTui-Search
Normal file
@@ -0,0 +1,26 @@
|
||||
Search Query: > ls
|
||||
|
||||
┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Hostname CWD Timestamp Runtime Exit Code Command │
|
||||
│────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
||||
│ localhost /tmp/ Oct 17 2022 21:43:16 PDT 3s 2 ls ~/ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
└────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
14
client/lib/goldens/TestTui-SmallTerminal
Normal file
14
client/lib/goldens/TestTui-SmallTerminal
Normal file
@@ -0,0 +1,14 @@
|
||||
Search Query: > ls
|
||||
|
||||
┌────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Hostname CWD Timestamp Runtime Exit Code Command │
|
||||
│────────────────────────────────────────────────────────────────────────────────────────│
|
||||
│ localhost /tmp/ Oct 17 2022 21:43:21 PDT 3s 2 echo 'aaaaaa bbbb' │
|
||||
│ localhost /tmp/ Oct 17 2022 21:43:16 PDT 3s 2 ls ~/ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
└────────────────────────────────────────────────────────────────────────────────────────┘
|
26
client/lib/goldens/testControlR-AdvancedSearch
Normal file
26
client/lib/goldens/testControlR-AdvancedSearch
Normal file
@@ -0,0 +1,26 @@
|
||||
Search Query: > cwd:/tmp/ ls
|
||||
|
||||
┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Hostname CWD Timestamp Runtime Exit Code Command │
|
||||
│────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
||||
│ localhost /tmp/ Oct 17 2022 21:43:26 PDT 3s 2 ls ~/bar/ │
|
||||
│ localhost /tmp/ Oct 17 2022 21:43:21 PDT 3s 2 ls ~/foo/ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
└────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
2
client/lib/goldens/testControlR-ControlC-bash
Normal file
2
client/lib/goldens/testControlR-ControlC-bash
Normal file
@@ -0,0 +1,2 @@
|
||||
bash-5.2$ source /Users/david/.bashrc
|
||||
bash-5.2$ echo
|
3
client/lib/goldens/testControlR-ControlC-fish
Normal file
3
client/lib/goldens/testControlR-ControlC-fish
Normal file
@@ -0,0 +1,3 @@
|
||||
Welcome to fish, the friendly interactive shell
|
||||
Type help for instructions on how to use fish
|
||||
david@Davids-MacBook-Air ~/c/hishtory (master)> echo 'aaaaaa bbbb'
|
1
client/lib/goldens/testControlR-ControlC-zsh
Normal file
1
client/lib/goldens/testControlR-ControlC-zsh
Normal file
@@ -0,0 +1 @@
|
||||
david@Davids-MacBook-Air hishtory % echo
|
26
client/lib/goldens/testControlR-Initial
Normal file
26
client/lib/goldens/testControlR-Initial
Normal file
@@ -0,0 +1,26 @@
|
||||
Search Query: > ls
|
||||
|
||||
┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Hostname CWD Timestamp Runtime Exit Code Command │
|
||||
│────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
||||
│ localhost /tmp/ Oct 17 2022 21:43:36 PDT 3s 2 echo 'bar' & │
|
||||
│ localhost /tmp/ Oct 17 2022 21:43:31 PDT 3s 2 echo 'aaaaaa bbbb' │
|
||||
│ localhost /tmp/ Oct 17 2022 21:43:26 PDT 3s 2 ls ~/bar/ │
|
||||
│ localhost /tmp/ Oct 17 2022 21:43:21 PDT 3s 2 ls ~/foo/ │
|
||||
│ server /etc/ Oct 17 2022 21:43:16 PDT 3s 127 ls ~/ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
└────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
26
client/lib/goldens/testControlR-InitialSearch
Normal file
26
client/lib/goldens/testControlR-InitialSearch
Normal file
@@ -0,0 +1,26 @@
|
||||
Search Query: > ls
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Hostname Exit Code Command foo │
|
||||
│─────────────────────────────────────────────────────────────────────────────│
|
||||
│ ghaction-runner-hostname 0 ls / foo │
|
||||
│ localhost 2 ls ~/bar/ │
|
||||
│ localhost 2 ls ~/foo/ │
|
||||
│ server 127 ls ~/ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
26
client/lib/goldens/testControlR-InitialSearchExpanded
Normal file
26
client/lib/goldens/testControlR-InitialSearchExpanded
Normal file
@@ -0,0 +1,26 @@
|
||||
Search Query: > echo
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Hostname Exit Code Command foo │
|
||||
│─────────────────────────────────────────────────────────────────────────────│
|
||||
│ localhost 2 echo 'bar' & │
|
||||
│ localhost 2 echo 'aaaaaa bbbb' │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
26
client/lib/goldens/testControlR-InitialSearchNoResults
Normal file
26
client/lib/goldens/testControlR-InitialSearchNoResults
Normal file
@@ -0,0 +1,26 @@
|
||||
Search Query: > asdf
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Hostname Exit Code Command foo │
|
||||
│─────────────────────────────────────────────────────────────────────────────│
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
@@ -0,0 +1,26 @@
|
||||
Search Query: > echo
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Hostname Exit Code Command foo │
|
||||
│─────────────────────────────────────────────────────────────────────────────│
|
||||
│ localhost 2 echo 'bar' & │
|
||||
│ localhost 2 echo 'aaaaaa bbbb' │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
26
client/lib/goldens/testControlR-Search
Normal file
26
client/lib/goldens/testControlR-Search
Normal file
@@ -0,0 +1,26 @@
|
||||
Search Query: > echo
|
||||
|
||||
┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Hostname CWD Timestamp Runtime Exit Code Command │
|
||||
│────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
||||
│ localhost /tmp/ Oct 17 2022 21:43:36 PDT 3s 2 echo 'bar' & │
|
||||
│ localhost /tmp/ Oct 17 2022 21:43:31 PDT 3s 2 echo 'aaaaaa bbbb' │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
└────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
2
client/lib/goldens/testControlR-bash-Disabled
Normal file
2
client/lib/goldens/testControlR-bash-Disabled
Normal file
@@ -0,0 +1,2 @@
|
||||
bash-5.2$ source /Users/david/.bashrc
|
||||
(reverse-i-search)`':
|
26
client/lib/goldens/testControlR-customColumn
Normal file
26
client/lib/goldens/testControlR-customColumn
Normal file
@@ -0,0 +1,26 @@
|
||||
Search Query: > -pipefail
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Hostname Exit Code Command foo │
|
||||
│─────────────────────────────────────────────────────────────────────────────│
|
||||
│ ghaction-runner-hostname 0 ls / foo │
|
||||
│ localhost 2 echo 'bar' & │
|
||||
│ localhost 2 echo 'aaaaaa bbbb' │
|
||||
│ localhost 2 ls ~/bar/ │
|
||||
│ localhost 2 ls ~/foo/ │
|
||||
│ server 127 ls ~/ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
26
client/lib/goldens/testControlR-displayedColumns
Normal file
26
client/lib/goldens/testControlR-displayedColumns
Normal file
@@ -0,0 +1,26 @@
|
||||
Search Query: > ls
|
||||
|
||||
┌────────────────────────────────────────────────────┐
|
||||
│ Hostname Exit Code Command │
|
||||
│────────────────────────────────────────────────────│
|
||||
│ localhost 2 echo 'bar' & │
|
||||
│ localhost 2 echo 'aaaaaa bbbb' │
|
||||
│ localhost 2 ls ~/bar/ │
|
||||
│ localhost 2 ls ~/foo/ │
|
||||
│ server 127 ls ~/ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
└────────────────────────────────────────────────────┘
|
3
client/lib/goldens/testControlR-fish-Disabled
Normal file
3
client/lib/goldens/testControlR-fish-Disabled
Normal file
@@ -0,0 +1,3 @@
|
||||
Welcome to fish, the friendly interactive shell
|
||||
Type help for instructions on how to use fish
|
||||
david@Davids-MacBook-Air ~/c/hishtory (master)>
|
2
client/lib/goldens/testControlR-zsh-Disabled
Normal file
2
client/lib/goldens/testControlR-zsh-Disabled
Normal file
@@ -0,0 +1,2 @@
|
||||
david@Davids-MacBook-Air hishtory %
|
||||
bck-i-search: _
|
4
client/lib/goldens/testCustomColumns-initHistory
Normal file
4
client/lib/goldens/testCustomColumns-initHistory
Normal file
@@ -0,0 +1,4 @@
|
||||
export FOOBAR='hello'
|
||||
echo $FOOBAR world
|
||||
cd /
|
||||
echo baz
|
10
client/lib/goldens/testCustomColumns-query-isAction=false
Normal file
10
client/lib/goldens/testCustomColumns-query-isAction=false
Normal file
@@ -0,0 +1,10 @@
|
||||
Exit Code git_remote Command
|
||||
0 git@github.com:ddworken/hishtory.git hishtory config-set displayed-columns 'Exit Code' git_remote Command
|
||||
0 echo bar
|
||||
0 cd /
|
||||
0 git@github.com:ddworken/hishtory.git echo foo
|
||||
0 git@github.com:ddworken/hishtory.git hishtory config-add custom-column git_remote '(git remote -v 2>/dev/null | grep origin 1>/dev/null ) && git remote get-url origin || true'
|
||||
0 echo baz
|
||||
0 cd /
|
||||
0 echo $FOOBAR world
|
||||
0 export FOOBAR='hello'
|
10
client/lib/goldens/testCustomColumns-query-isAction=true
Normal file
10
client/lib/goldens/testCustomColumns-query-isAction=true
Normal file
@@ -0,0 +1,10 @@
|
||||
Exit Code git_remote Command
|
||||
0 https://github.com/ddworken/hishtory hishtory config-set displayed-columns 'Exit Code' git_remote Command
|
||||
0 echo bar
|
||||
0 cd /
|
||||
0 https://github.com/ddworken/hishtory echo foo
|
||||
0 https://github.com/ddworken/hishtory hishtory config-add custom-column git_remote '(git remote -v 2>/dev/null | grep origin 1>/dev/null ) && git remote get-url origin || true'
|
||||
0 echo baz
|
||||
0 cd /
|
||||
0 echo $FOOBAR world
|
||||
0 export FOOBAR='hello'
|
31
client/lib/goldens/testCustomColumns-tquery-bash
Normal file
31
client/lib/goldens/testCustomColumns-tquery-bash
Normal file
@@ -0,0 +1,31 @@
|
||||
bash-5.2$ source /Users/david/.bashrc
|
||||
bash-5.2$ hishtory tquery -pipefail
|
||||
|
||||
|
||||
|
||||
Search Query: > -pipefail
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Exit Code git_remote Command │
|
||||
│─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
||||
│ 0 git@github.com:ddworken/hishtory.git hishtory config-set displayed-columns 'Exit Code' git_remote Command │
|
||||
│ 0 echo bar │
|
||||
│ 0 cd / │
|
||||
│ 0 git@github.com:ddworken/hishtory.git echo foo │
|
||||
│ 0 git@github.com:ddworken/hishtory.git hishtory config-add custom-column git_remote '(git remote -v 2>/dev/null | grep origin 1>/dev/null ) && git remote get-url origin || … │
|
||||
│ 0 echo baz │
|
||||
│ 0 cd / │
|
||||
│ 0 echo $FOOBAR world │
|
||||
│ 0 export FOOBAR='hello' │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
@@ -0,0 +1,31 @@
|
||||
bash-5.2$ source /Users/runner/.bashrc
|
||||
bash-5.2$ hishtory tquery -pipefail
|
||||
|
||||
|
||||
|
||||
Search Query: > -pipefail
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Exit Code git_remote Command │
|
||||
│─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
||||
│ 0 https://github.com/ddworken/hishtory hishtory config-set displayed-columns 'Exit Code' git_remote Command │
|
||||
│ 0 echo bar │
|
||||
│ 0 cd / │
|
||||
│ 0 https://github.com/ddworken/hishtory echo foo │
|
||||
│ 0 https://github.com/ddworken/hishtory hishtory config-add custom-column git_remote '(git remote -v 2>/dev/null | grep origin 1>/dev/null ) && git remote get-url origin || … │
|
||||
│ 0 echo baz │
|
||||
│ 0 cd / │
|
||||
│ 0 echo $FOOBAR world │
|
||||
│ 0 export FOOBAR='hello' │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
@@ -0,0 +1,31 @@
|
||||
runner@ghaction-runner-hostname:~/work/hishtory/hishtory$ source /home/runner/.bashrc
|
||||
runner@ghaction-runner-hostname:~/work/hishtory/hishtory$ hishtory tquery -pipefail
|
||||
|
||||
|
||||
|
||||
Search Query: > -pipefail
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Exit Code git_remote Command │
|
||||
│─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
||||
│ 0 https://github.com/ddworken/hishtory hishtory config-set displayed-columns 'Exit Code' git_remote Command │
|
||||
│ 0 echo bar │
|
||||
│ 0 cd / │
|
||||
│ 0 https://github.com/ddworken/hishtory echo foo │
|
||||
│ 0 https://github.com/ddworken/hishtory hishtory config-add custom-column git_remote '(git remote -v 2>/dev/null | grep origin 1>/dev/null ) && git remote get-url origin || … │
|
||||
│ 0 echo baz │
|
||||
│ 0 cd / │
|
||||
│ 0 echo $FOOBAR world │
|
||||
│ 0 export FOOBAR='hello' │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
30
client/lib/goldens/testCustomColumns-tquery-zsh
Normal file
30
client/lib/goldens/testCustomColumns-tquery-zsh
Normal file
@@ -0,0 +1,30 @@
|
||||
david@Davids-MacBook-Air hishtory % hishtory tquery -pipefail
|
||||
|
||||
|
||||
|
||||
Search Query: > -pipefail
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Exit Code git_remote Command │
|
||||
│─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
||||
│ 0 git@github.com:ddworken/hishtory.git hishtory config-set displayed-columns 'Exit Code' git_remote Command │
|
||||
│ 0 echo bar │
|
||||
│ 0 cd / │
|
||||
│ 0 git@github.com:ddworken/hishtory.git echo foo │
|
||||
│ 0 git@github.com:ddworken/hishtory.git hishtory config-add custom-column git_remote '(git remote -v 2>/dev/null | grep origin 1>/dev/null ) && git remote get-url origin || … │
|
||||
│ 0 echo baz │
|
||||
│ 0 cd / │
|
||||
│ 0 echo $FOOBAR world │
|
||||
│ 0 export FOOBAR='hello' │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
@@ -0,0 +1,30 @@
|
||||
runner@ghaction-runner-hostname hishtory % hishtory tquery -pipefail
|
||||
|
||||
|
||||
|
||||
Search Query: > -pipefail
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Exit Code git_remote Command │
|
||||
│─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
||||
│ 0 https://github.com/ddworken/hishtory hishtory config-set displayed-columns 'Exit Code' git_remote Command │
|
||||
│ 0 echo bar │
|
||||
│ 0 cd / │
|
||||
│ 0 https://github.com/ddworken/hishtory echo foo │
|
||||
│ 0 https://github.com/ddworken/hishtory hishtory config-add custom-column git_remote '(git remote -v 2>/dev/null | grep origin 1>/dev/null ) && git remote get-url origin || … │
|
||||
│ 0 echo baz │
|
||||
│ 0 cd / │
|
||||
│ 0 echo $FOOBAR world │
|
||||
│ 0 export FOOBAR='hello' │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
@@ -0,0 +1,30 @@
|
||||
ghaction-runner-hostname% hishtory tquery -pipefail
|
||||
|
||||
|
||||
|
||||
Search Query: > -pipefail
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Exit Code git_remote Command │
|
||||
│─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────│
|
||||
│ 0 https://github.com/ddworken/hishtory hishtory config-set displayed-columns 'Exit Code' git_remote Command │
|
||||
│ 0 echo bar │
|
||||
│ 0 cd / │
|
||||
│ 0 https://github.com/ddworken/hishtory echo foo │
|
||||
│ 0 https://github.com/ddworken/hishtory hishtory config-add custom-column git_remote '(git remote -v 2>/dev/null | grep origin 1>/dev/null ) && git remote get-url origin || … │
|
||||
│ 0 echo baz │
|
||||
│ 0 cd / │
|
||||
│ 0 echo $FOOBAR world │
|
||||
│ 0 export FOOBAR='hello' │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
|
3
client/lib/goldens/testDisplayTable-customColumns
Normal file
3
client/lib/goldens/testDisplayTable-customColumns
Normal file
@@ -0,0 +1,3 @@
|
||||
Hostname Command
|
||||
localhost table_cmd2
|
||||
localhost table_cmd1
|
3
client/lib/goldens/testDisplayTable-customColumns-2
Normal file
3
client/lib/goldens/testDisplayTable-customColumns-2
Normal file
@@ -0,0 +1,3 @@
|
||||
Hostname Exit Code Command
|
||||
localhost 3 table_cmd2
|
||||
localhost 2 table_cmd1
|
3
client/lib/goldens/testDisplayTable-customColumns-3
Normal file
3
client/lib/goldens/testDisplayTable-customColumns-3
Normal file
@@ -0,0 +1,3 @@
|
||||
Hostname Exit Code Command CWD
|
||||
localhost 3 table_cmd2 ~/foo/
|
||||
localhost 2 table_cmd1 /tmp/
|
@@ -0,0 +1,7 @@
|
||||
Hostname Exit Code Command CWD
|
||||
localhost 2 while : /tmp/
|
||||
do
|
||||
ls /table/
|
||||
done
|
||||
localhost 3 table_cmd2 ~/foo/
|
||||
localhost 2 table_cmd1 /tmp/
|
@@ -0,0 +1,9 @@
|
||||
Hostname Exit Code Command CWD foo
|
||||
ghaction-runner-hostname 0 echo table-2 / aaaaaaaaaaaaa
|
||||
ghaction-runner-hostname 0 echo table-1 / aaaaaaaaaaaaa
|
||||
localhost 2 while : /tmp/
|
||||
do
|
||||
ls /table/
|
||||
done
|
||||
localhost 3 table_cmd2 ~/foo/
|
||||
localhost 2 table_cmd1 /tmp/
|
3
client/lib/goldens/testDisplayTable-defaultColumns
Normal file
3
client/lib/goldens/testDisplayTable-defaultColumns
Normal file
@@ -0,0 +1,3 @@
|
||||
Hostname CWD Timestamp Runtime Exit Code Command
|
||||
localhost ~/foo/ Apr 16 2022 01:03:16 PDT 24s 3 table_cmd2
|
||||
localhost /tmp/ Apr 16 2022 01:03:06 PDT 4s 2 table_cmd1
|
38
client/lib/goldens/testIntegrationWithNewDevice-bash
Normal file
38
client/lib/goldens/testIntegrationWithNewDevice-bash
Normal file
@@ -0,0 +1,38 @@
|
||||
set -emo pipefail
|
||||
hishtory status
|
||||
set -emo pipefail
|
||||
hishtory query
|
||||
ls /a
|
||||
ls /bar
|
||||
ls /foo
|
||||
echo foo
|
||||
echo bar
|
||||
hishtory enable
|
||||
echo thisisrecorded
|
||||
set -emo pipefail
|
||||
hishtory query
|
||||
set -emo pipefail
|
||||
hishtory query foo
|
||||
echo hello | grep complex | sed s/h/i/g; echo baz && echo "fo 'o" # mycommand
|
||||
set -emo pipefail
|
||||
hishtory query complex
|
||||
set -emo pipefail
|
||||
hishtory query
|
||||
set -emo pipefail
|
||||
echo mynewcommand
|
||||
set -emo pipefail
|
||||
hishtory query
|
||||
set -emo pipefail
|
||||
hishtory query
|
||||
set -emo pipefail
|
||||
echo mynewercommand
|
||||
set -emo pipefail
|
||||
hishtory query
|
||||
othercomputer
|
||||
set -emo pipefail
|
||||
hishtory query
|
||||
set -emo pipefail
|
||||
hishtory reupload
|
||||
set -emo pipefail
|
||||
hishtory export | grep -v pipefail | grep -v '/tmp/client install'
|
||||
set -emo pipefail
|
24
client/lib/goldens/testIntegrationWithNewDevice-tablebash
Normal file
24
client/lib/goldens/testIntegrationWithNewDevice-tablebash
Normal file
@@ -0,0 +1,24 @@
|
||||
Hostname Exit Code Command
|
||||
ghaction-runner-hostname 0 set -emo pipefail
|
||||
ghaction-runner-hostname 0 hishtory config-set displayed-columns Hostname 'Exit Code' Command
|
||||
ghaction-runner-hostname 0 set -emo pipefail
|
||||
ghaction-runner-hostname 0 set -emo pipefail
|
||||
ghaction-runner-hostname 0 hishtory export | grep -v pipefail | grep -v '/tmp/client install'
|
||||
ghaction-runner-hostname 0 set -emo pipefail
|
||||
ghaction-runner-hostname 0 hishtory reupload
|
||||
ghaction-runner-hostname 0 set -emo pipefail
|
||||
ghaction-runner-hostname 0 hishtory query
|
||||
ghaction-runner-hostname 0 set -emo pipefail
|
||||
localhost 2 othercomputer
|
||||
ghaction-runner-hostname 0 hishtory query
|
||||
ghaction-runner-hostname 0 set -emo pipefail
|
||||
ghaction-runner-hostname 0 echo mynewercommand
|
||||
ghaction-runner-hostname 0 set -emo pipefail
|
||||
ghaction-runner-hostname 0 hishtory query
|
||||
ghaction-runner-hostname 0 set -emo pipefail
|
||||
ghaction-runner-hostname 0 hishtory query
|
||||
ghaction-runner-hostname 0 set -emo pipefail
|
||||
ghaction-runner-hostname 0 echo mynewcommand
|
||||
ghaction-runner-hostname 0 set -emo pipefail
|
||||
ghaction-runner-hostname 0 hishtory query
|
||||
ghaction-runner-hostname 0 set -emo pipefail
|
24
client/lib/goldens/testIntegrationWithNewDevice-tablezsh
Normal file
24
client/lib/goldens/testIntegrationWithNewDevice-tablezsh
Normal file
@@ -0,0 +1,24 @@
|
||||
Hostname Exit Code Command
|
||||
ghaction-runner-hostname 0 set -eo pipefail
|
||||
ghaction-runner-hostname 0 hishtory config-set displayed-columns Hostname 'Exit Code' Command
|
||||
ghaction-runner-hostname 0 set -eo pipefail
|
||||
ghaction-runner-hostname 0 set -eo pipefail
|
||||
ghaction-runner-hostname 0 hishtory export | grep -v pipefail | grep -v '/tmp/client install'
|
||||
ghaction-runner-hostname 0 set -eo pipefail
|
||||
ghaction-runner-hostname 0 hishtory reupload
|
||||
ghaction-runner-hostname 0 set -eo pipefail
|
||||
ghaction-runner-hostname 0 hishtory query
|
||||
ghaction-runner-hostname 0 set -eo pipefail
|
||||
localhost 2 othercomputer
|
||||
ghaction-runner-hostname 0 hishtory query
|
||||
ghaction-runner-hostname 0 set -eo pipefail
|
||||
ghaction-runner-hostname 0 echo mynewercommand
|
||||
ghaction-runner-hostname 0 set -eo pipefail
|
||||
ghaction-runner-hostname 0 hishtory query
|
||||
ghaction-runner-hostname 0 set -eo pipefail
|
||||
ghaction-runner-hostname 0 hishtory query
|
||||
ghaction-runner-hostname 0 set -eo pipefail
|
||||
ghaction-runner-hostname 0 echo mynewcommand
|
||||
ghaction-runner-hostname 0 set -eo pipefail
|
||||
ghaction-runner-hostname 0 hishtory query
|
||||
ghaction-runner-hostname 0 set -eo pipefail
|
38
client/lib/goldens/testIntegrationWithNewDevice-zsh
Normal file
38
client/lib/goldens/testIntegrationWithNewDevice-zsh
Normal file
@@ -0,0 +1,38 @@
|
||||
set -eo pipefail
|
||||
hishtory status
|
||||
set -eo pipefail
|
||||
hishtory query
|
||||
ls /a
|
||||
ls /bar
|
||||
ls /foo
|
||||
echo foo
|
||||
echo bar
|
||||
hishtory enable
|
||||
echo thisisrecorded
|
||||
set -eo pipefail
|
||||
hishtory query
|
||||
set -eo pipefail
|
||||
hishtory query foo
|
||||
echo hello | grep complex | sed s/h/i/g; echo baz && echo "fo 'o" # mycommand
|
||||
set -eo pipefail
|
||||
hishtory query complex
|
||||
set -eo pipefail
|
||||
hishtory query
|
||||
set -eo pipefail
|
||||
echo mynewcommand
|
||||
set -eo pipefail
|
||||
hishtory query
|
||||
set -eo pipefail
|
||||
hishtory query
|
||||
set -eo pipefail
|
||||
echo mynewercommand
|
||||
set -eo pipefail
|
||||
hishtory query
|
||||
othercomputer
|
||||
set -eo pipefail
|
||||
hishtory query
|
||||
set -eo pipefail
|
||||
hishtory reupload
|
||||
set -eo pipefail
|
||||
hishtory export | grep -v pipefail | grep -v '/tmp/client install'
|
||||
set -eo pipefail
|
@@ -0,0 +1,7 @@
|
||||
echo foo
|
||||
echo foo
|
||||
echo baz
|
||||
echo baz
|
||||
echo foo
|
||||
hishtory config-set displayed-columns 'Exit Code' Command
|
||||
hishtory config-set filter-duplicate-commands true
|
6
client/lib/goldens/testRemoveDuplicateRows-enabled-query
Normal file
6
client/lib/goldens/testRemoveDuplicateRows-enabled-query
Normal file
@@ -0,0 +1,6 @@
|
||||
Exit Code Command
|
||||
0 hishtory config-set filter-duplicate-commands true
|
||||
0 hishtory config-set displayed-columns 'Exit Code' Command
|
||||
0 echo foo
|
||||
0 echo baz
|
||||
0 echo foo
|
30
client/lib/goldens/testRemoveDuplicateRows-enabled-tquery
Normal file
30
client/lib/goldens/testRemoveDuplicateRows-enabled-tquery
Normal file
@@ -0,0 +1,30 @@
|
||||
-pipefail
|
||||
|
||||
|
||||
|
||||
Search Query: > -pipefail
|
||||
|
||||
┌───────────────────────────────────────────────────────────────────────────┐
|
||||
│ Exit Code Command │
|
||||
│───────────────────────────────────────────────────────────────────────────│
|
||||
│ 0 hishtory config-set filter-duplicate-commands true │
|
||||
│ 0 hishtory config-set displayed-columns 'Exit Code' Command │
|
||||
│ 0 echo foo │
|
||||
│ 0 echo baz │
|
||||
│ 0 echo foo │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
└───────────────────────────────────────────────────────────────────────────┘
|
5
client/lib/goldens/testRemoveDuplicateRows-export
Normal file
5
client/lib/goldens/testRemoveDuplicateRows-export
Normal file
@@ -0,0 +1,5 @@
|
||||
echo foo
|
||||
echo foo
|
||||
echo baz
|
||||
echo baz
|
||||
echo foo
|
7
client/lib/goldens/testRemoveDuplicateRows-query
Normal file
7
client/lib/goldens/testRemoveDuplicateRows-query
Normal file
@@ -0,0 +1,7 @@
|
||||
Exit Code Command
|
||||
0 hishtory config-set displayed-columns 'Exit Code' Command
|
||||
0 echo foo
|
||||
0 echo baz
|
||||
0 echo baz
|
||||
0 echo foo
|
||||
0 echo foo
|
30
client/lib/goldens/testRemoveDuplicateRows-tquery
Normal file
30
client/lib/goldens/testRemoveDuplicateRows-tquery
Normal file
@@ -0,0 +1,30 @@
|
||||
-pipefail
|
||||
|
||||
|
||||
|
||||
Search Query: > -pipefail
|
||||
|
||||
┌───────────────────────────────────────────────────────────────────────────┐
|
||||
│ Exit Code Command │
|
||||
│───────────────────────────────────────────────────────────────────────────│
|
||||
│ 0 hishtory config-set displayed-columns 'Exit Code' Command │
|
||||
│ 0 echo foo │
|
||||
│ 0 echo baz │
|
||||
│ 0 echo baz │
|
||||
│ 0 echo foo │
|
||||
│ 0 echo foo │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
└───────────────────────────────────────────────────────────────────────────┘
|
2
client/lib/goldens/testUninstall-post-uninstall
Normal file
2
client/lib/goldens/testUninstall-post-uninstall
Normal file
@@ -0,0 +1,2 @@
|
||||
foo
|
||||
bar
|
8
client/lib/goldens/testUninstall-post-uninstall-bash
Normal file
8
client/lib/goldens/testUninstall-post-uninstall-bash
Normal file
@@ -0,0 +1,8 @@
|
||||
bash-5.2$ source /Users/david/.bashrc
|
||||
bash-5.2$ echo foo
|
||||
foo
|
||||
bash-5.2$ hishtory
|
||||
bash: hishtory: command not found
|
||||
bash-5.2$ echo bar
|
||||
bar
|
||||
bash-5.2$
|
7
client/lib/goldens/testUninstall-post-uninstall-zsh
Normal file
7
client/lib/goldens/testUninstall-post-uninstall-zsh
Normal file
@@ -0,0 +1,7 @@
|
||||
david@Davids-MacBook-Air hishtory % echo foo
|
||||
foo
|
||||
david@Davids-MacBook-Air hishtory % hishtory
|
||||
zsh: command not found: hishtory
|
||||
david@Davids-MacBook-Air hishtory % echo bar
|
||||
bar
|
||||
david@Davids-MacBook-Air hishtory %
|
2
client/lib/goldens/testUninstall-recorded
Normal file
2
client/lib/goldens/testUninstall-recorded
Normal file
@@ -0,0 +1,2 @@
|
||||
echo foo
|
||||
echo baz
|
1
client/lib/goldens/testUninstall-uninstall
Normal file
1
client/lib/goldens/testUninstall-uninstall
Normal file
@@ -0,0 +1 @@
|
||||
Are you sure you want to uninstall hiSHtory and delete all locally saved history data [y/N]Successfully uninstalled hishtory, please restart your terminal...
|
1700
client/lib/lib.go
Normal file
1700
client/lib/lib.go
Normal file
File diff suppressed because it is too large
Load Diff
435
client/lib/lib_test.go
Normal file
435
client/lib/lib_test.go
Normal file
@@ -0,0 +1,435 @@
|
||||
package lib
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/user"
|
||||
"path"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ddworken/hishtory/client/data"
|
||||
"github.com/ddworken/hishtory/client/hctx"
|
||||
"github.com/ddworken/hishtory/shared/testutils"
|
||||
)
|
||||
|
||||
func TestSetup(t *testing.T) {
|
||||
defer testutils.BackupAndRestore(t)()
|
||||
defer testutils.RunTestServer()()
|
||||
|
||||
homedir, err := os.UserHomeDir()
|
||||
testutils.Check(t, err)
|
||||
if _, err := os.Stat(path.Join(homedir, data.HISHTORY_PATH, data.CONFIG_PATH)); err == nil {
|
||||
t.Fatalf("hishtory secret file already exists!")
|
||||
}
|
||||
testutils.Check(t, Setup([]string{}))
|
||||
if _, err := os.Stat(path.Join(homedir, data.HISHTORY_PATH, data.CONFIG_PATH)); err != nil {
|
||||
t.Fatalf("hishtory secret file does not exist after Setup()!")
|
||||
}
|
||||
data, err := os.ReadFile(path.Join(homedir, data.HISHTORY_PATH, data.CONFIG_PATH))
|
||||
testutils.Check(t, err)
|
||||
if len(data) < 10 {
|
||||
t.Fatalf("hishtory secret has unexpected length: %d", len(data))
|
||||
}
|
||||
config := hctx.GetConf(hctx.MakeContext())
|
||||
if config.IsOffline != false {
|
||||
t.Fatalf("hishtory config should have been offline")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupOffline(t *testing.T) {
|
||||
defer testutils.BackupAndRestore(t)()
|
||||
defer testutils.RunTestServer()()
|
||||
|
||||
homedir, err := os.UserHomeDir()
|
||||
testutils.Check(t, err)
|
||||
if _, err := os.Stat(path.Join(homedir, data.HISHTORY_PATH, data.CONFIG_PATH)); err == nil {
|
||||
t.Fatalf("hishtory secret file already exists!")
|
||||
}
|
||||
testutils.Check(t, Setup([]string{"", "", "--offline"}))
|
||||
if _, err := os.Stat(path.Join(homedir, data.HISHTORY_PATH, data.CONFIG_PATH)); err != nil {
|
||||
t.Fatalf("hishtory secret file does not exist after Setup()!")
|
||||
}
|
||||
data, err := os.ReadFile(path.Join(homedir, data.HISHTORY_PATH, data.CONFIG_PATH))
|
||||
testutils.Check(t, err)
|
||||
if len(data) < 10 {
|
||||
t.Fatalf("hishtory secret has unexpected length: %d", len(data))
|
||||
}
|
||||
config := hctx.GetConf(hctx.MakeContext())
|
||||
if config.IsOffline != true {
|
||||
t.Fatalf("hishtory config should have been offline, actual=%#v", string(data))
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildHistoryEntry(t *testing.T) {
|
||||
defer testutils.BackupAndRestore(t)()
|
||||
defer testutils.RunTestServer()()
|
||||
testutils.Check(t, Setup([]string{}))
|
||||
|
||||
// Test building an actual entry for bash
|
||||
entry, err := BuildHistoryEntry(hctx.MakeContext(), []string{"unused", "saveHistoryEntry", "bash", "120", " 123 ls /foo ", "1641774958"})
|
||||
testutils.Check(t, err)
|
||||
if entry.ExitCode != 120 {
|
||||
t.Fatalf("history entry has unexpected exit code: %v", entry.ExitCode)
|
||||
}
|
||||
user, err := user.Current()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to retrieve user: %v", err)
|
||||
}
|
||||
if entry.LocalUsername != user.Username {
|
||||
t.Fatalf("history entry has unexpected user name: %v", entry.LocalUsername)
|
||||
}
|
||||
if !strings.HasPrefix(entry.CurrentWorkingDirectory, "/") && !strings.HasPrefix(entry.CurrentWorkingDirectory, "~/") {
|
||||
t.Fatalf("history entry has unexpected cwd: %v", entry.CurrentWorkingDirectory)
|
||||
}
|
||||
if !strings.HasPrefix(entry.HomeDirectory, "/") {
|
||||
t.Fatalf("history entry has unexpected home directory: %v", entry.HomeDirectory)
|
||||
}
|
||||
if entry.Command != "ls /foo" {
|
||||
t.Fatalf("history entry has unexpected command: %v", entry.Command)
|
||||
}
|
||||
if !strings.HasPrefix(entry.StartTime.Format(time.RFC3339), "2022-01-09T") && !strings.HasPrefix(entry.StartTime.Format(time.RFC3339), "2022-01-10T") {
|
||||
t.Fatalf("history entry has incorrect date in the start time: %v", entry.StartTime.Format(time.RFC3339))
|
||||
}
|
||||
if entry.StartTime.Unix() != 1641774958 {
|
||||
t.Fatalf("history entry has incorrect Unix time in the start time: %v", entry.StartTime.Unix())
|
||||
}
|
||||
|
||||
// Test building an entry for zsh
|
||||
entry, err = BuildHistoryEntry(hctx.MakeContext(), []string{"unused", "saveHistoryEntry", "zsh", "120", "ls /foo\n", "1641774958"})
|
||||
testutils.Check(t, err)
|
||||
if entry.ExitCode != 120 {
|
||||
t.Fatalf("history entry has unexpected exit code: %v", entry.ExitCode)
|
||||
}
|
||||
if entry.LocalUsername != user.Username {
|
||||
t.Fatalf("history entry has unexpected user name: %v", entry.LocalUsername)
|
||||
}
|
||||
if !strings.HasPrefix(entry.CurrentWorkingDirectory, "/") && !strings.HasPrefix(entry.CurrentWorkingDirectory, "~/") {
|
||||
t.Fatalf("history entry has unexpected cwd: %v", entry.CurrentWorkingDirectory)
|
||||
}
|
||||
if !strings.HasPrefix(entry.HomeDirectory, "/") {
|
||||
t.Fatalf("history entry has unexpected home directory: %v", entry.HomeDirectory)
|
||||
}
|
||||
if entry.Command != "ls /foo" {
|
||||
t.Fatalf("history entry has unexpected command: %v", entry.Command)
|
||||
}
|
||||
if !strings.HasPrefix(entry.StartTime.Format(time.RFC3339), "2022-01-09T") && !strings.HasPrefix(entry.StartTime.Format(time.RFC3339), "2022-01-10T") {
|
||||
t.Fatalf("history entry has incorrect date in the start time: %v", entry.StartTime.Format(time.RFC3339))
|
||||
}
|
||||
if entry.StartTime.Unix() != 1641774958 {
|
||||
t.Fatalf("history entry has incorrect Unix time in the start time: %v", entry.StartTime.Unix())
|
||||
}
|
||||
|
||||
// Test building an entry for fish
|
||||
entry, err = BuildHistoryEntry(hctx.MakeContext(), []string{"unused", "saveHistoryEntry", "fish", "120", "ls /foo\n", "1641774958"})
|
||||
testutils.Check(t, err)
|
||||
if entry.ExitCode != 120 {
|
||||
t.Fatalf("history entry has unexpected exit code: %v", entry.ExitCode)
|
||||
}
|
||||
if entry.LocalUsername != user.Username {
|
||||
t.Fatalf("history entry has unexpected user name: %v", entry.LocalUsername)
|
||||
}
|
||||
if !strings.HasPrefix(entry.CurrentWorkingDirectory, "/") && !strings.HasPrefix(entry.CurrentWorkingDirectory, "~/") {
|
||||
t.Fatalf("history entry has unexpected cwd: %v", entry.CurrentWorkingDirectory)
|
||||
}
|
||||
if !strings.HasPrefix(entry.HomeDirectory, "/") {
|
||||
t.Fatalf("history entry has unexpected home directory: %v", entry.HomeDirectory)
|
||||
}
|
||||
if entry.Command != "ls /foo" {
|
||||
t.Fatalf("history entry has unexpected command: %v", entry.Command)
|
||||
}
|
||||
if !strings.HasPrefix(entry.StartTime.Format(time.RFC3339), "2022-01-09T") && !strings.HasPrefix(entry.StartTime.Format(time.RFC3339), "2022-01-10T") {
|
||||
t.Fatalf("history entry has incorrect date in the start time: %v", entry.StartTime.Format(time.RFC3339))
|
||||
}
|
||||
if entry.StartTime.Unix() != 1641774958 {
|
||||
t.Fatalf("history entry has incorrect Unix time in the start time: %v", entry.StartTime.Unix())
|
||||
}
|
||||
|
||||
// Test building an entry that is empty, and thus not saved
|
||||
entry, err = BuildHistoryEntry(hctx.MakeContext(), []string{"unused", "saveHistoryEntry", "zsh", "120", " \n", "1641774958"})
|
||||
testutils.Check(t, err)
|
||||
if entry != nil {
|
||||
t.Fatalf("expected history entry to be nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildHistoryEntryWithTimestampStripping(t *testing.T) {
|
||||
defer testutils.BackupAndRestoreEnv("HISTTIMEFORMAT")()
|
||||
defer testutils.BackupAndRestore(t)()
|
||||
defer testutils.RunTestServer()()
|
||||
testutils.Check(t, Setup([]string{}))
|
||||
|
||||
testcases := []struct {
|
||||
input, histtimeformat, expectedCommand string
|
||||
}{
|
||||
{" 123 ls /foo ", "", "ls /foo"},
|
||||
{" 2389 [2022-09-28 04:38:32 +0000] echo", "", "[2022-09-28 04:38:32 +0000] echo"},
|
||||
{" 2389 [2022-09-28 04:38:32 +0000] echo", "[%F %T %z] ", "echo"},
|
||||
}
|
||||
for _, tc := range testcases {
|
||||
conf := hctx.GetConf(hctx.MakeContext())
|
||||
conf.LastSavedHistoryLine = ""
|
||||
testutils.Check(t, hctx.SetConfig(conf))
|
||||
|
||||
os.Setenv("HISTTIMEFORMAT", tc.histtimeformat)
|
||||
entry, err := BuildHistoryEntry(hctx.MakeContext(), []string{"unused", "saveHistoryEntry", "bash", "120", tc.input, "1641774958"})
|
||||
testutils.Check(t, err)
|
||||
if entry == nil {
|
||||
t.Fatalf("entry is unexpectedly nil")
|
||||
}
|
||||
if entry.Command != tc.expectedCommand {
|
||||
t.Fatalf("BuildHistoryEntry(%#v) returned %#v (expected=%#v)", tc.input, entry.Command, tc.expectedCommand)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPersist(t *testing.T) {
|
||||
defer testutils.BackupAndRestore(t)()
|
||||
testutils.Check(t, hctx.InitConfig())
|
||||
db := hctx.GetDb(hctx.MakeContext())
|
||||
|
||||
entry := testutils.MakeFakeHistoryEntry("ls ~/")
|
||||
db.Create(entry)
|
||||
var historyEntries []*data.HistoryEntry
|
||||
result := db.Find(&historyEntries)
|
||||
testutils.Check(t, result.Error)
|
||||
if len(historyEntries) != 1 {
|
||||
t.Fatalf("DB has %d entries, expected 1!", len(historyEntries))
|
||||
}
|
||||
dbEntry := historyEntries[0]
|
||||
if !data.EntryEquals(entry, *dbEntry) {
|
||||
t.Fatalf("DB data is different than input! \ndb =%#v \ninput=%#v", *dbEntry, entry)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSearch(t *testing.T) {
|
||||
defer testutils.BackupAndRestore(t)()
|
||||
testutils.Check(t, hctx.InitConfig())
|
||||
ctx := hctx.MakeContext()
|
||||
db := hctx.GetDb(ctx)
|
||||
|
||||
// Insert data
|
||||
entry1 := testutils.MakeFakeHistoryEntry("ls /foo")
|
||||
db.Create(entry1)
|
||||
entry2 := testutils.MakeFakeHistoryEntry("ls /bar")
|
||||
db.Create(entry2)
|
||||
|
||||
// Search for data
|
||||
results, err := Search(ctx, db, "ls", 5)
|
||||
testutils.Check(t, err)
|
||||
if len(results) != 2 {
|
||||
t.Fatalf("Search() returned %d results, expected 2!", len(results))
|
||||
}
|
||||
if !data.EntryEquals(*results[0], entry2) {
|
||||
t.Fatalf("Search()[0]=%#v, expected: %#v", results[0], entry2)
|
||||
}
|
||||
if !data.EntryEquals(*results[1], entry1) {
|
||||
t.Fatalf("Search()[0]=%#v, expected: %#v", results[1], entry1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddToDbIfNew(t *testing.T) {
|
||||
// Set up
|
||||
defer testutils.BackupAndRestore(t)()
|
||||
testutils.Check(t, hctx.InitConfig())
|
||||
db := hctx.GetDb(hctx.MakeContext())
|
||||
|
||||
// Add duplicate entries
|
||||
entry1 := testutils.MakeFakeHistoryEntry("ls /foo")
|
||||
AddToDbIfNew(db, entry1)
|
||||
AddToDbIfNew(db, entry1)
|
||||
entry2 := testutils.MakeFakeHistoryEntry("ls /foo")
|
||||
AddToDbIfNew(db, entry2)
|
||||
AddToDbIfNew(db, entry2)
|
||||
AddToDbIfNew(db, entry1)
|
||||
|
||||
// Check there should only be two entries
|
||||
var entries []data.HistoryEntry
|
||||
result := db.Find(&entries)
|
||||
if result.Error != nil {
|
||||
t.Fatal(result.Error)
|
||||
}
|
||||
if len(entries) != 2 {
|
||||
t.Fatalf("entries has an incorrect length: %d", len(entries))
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseCrossPlatformInt(t *testing.T) {
|
||||
res, err := parseCrossPlatformInt("123")
|
||||
testutils.Check(t, err)
|
||||
if res != 123 {
|
||||
t.Fatalf("failed to parse cross platform int %d", res)
|
||||
}
|
||||
res, err = parseCrossPlatformInt("123N")
|
||||
testutils.Check(t, err)
|
||||
if res != 123 {
|
||||
t.Fatalf("failed to parse cross platform int %d", res)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildRegexFromTimeFormat(t *testing.T) {
|
||||
testcases := []struct {
|
||||
formatString, regex string
|
||||
}{
|
||||
{"%Y ", "[0-9]{4} "},
|
||||
{"%F %T ", "[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} "},
|
||||
{"%F%T", "[0-9]{4}-[0-9]{2}-[0-9]{2}[0-9]{2}:[0-9]{2}:[0-9]{2}"},
|
||||
{"%%", "%"},
|
||||
{"%%%%", "%%"},
|
||||
{"%%%Y", "%[0-9]{4}"},
|
||||
{"%%%F%T", "%[0-9]{4}-[0-9]{2}-[0-9]{2}[0-9]{2}:[0-9]{2}:[0-9]{2}"},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
if regex := buildRegexFromTimeFormat(tc.formatString); regex != tc.regex {
|
||||
t.Fatalf("building a regex for %#v returned %#v (expected=%#v)", tc.formatString, regex, tc.regex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetLastCommand(t *testing.T) {
|
||||
testcases := []struct {
|
||||
input, expectedOutput string
|
||||
}{
|
||||
{" 0 ls", "ls"},
|
||||
{" 33 ls", "ls"},
|
||||
{" 33 ls --aaaa foo bar ", "ls --aaaa foo bar"},
|
||||
{" 2389 [2022-09-28 04:38:32 +0000] echo", "[2022-09-28 04:38:32 +0000] echo"},
|
||||
}
|
||||
for _, tc := range testcases {
|
||||
actualOutput, err := getLastCommand(tc.input)
|
||||
testutils.Check(t, err)
|
||||
if actualOutput != tc.expectedOutput {
|
||||
t.Fatalf("getLastCommand(%#v) returned %#v (expected=%#v)", tc.input, actualOutput, tc.expectedOutput)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaybeSkipBashHistTimePrefix(t *testing.T) {
|
||||
defer testutils.BackupAndRestoreEnv("HISTTIMEFORMAT")()
|
||||
|
||||
testcases := []struct {
|
||||
env, cmdLine, expected string
|
||||
}{
|
||||
{"%F %T ", "2019-07-12 13:02:31 sudo apt update", "sudo apt update"},
|
||||
{"%F %T ", "2019-07-12 13:02:31 ls a b", "ls a b"},
|
||||
{"%F %T ", "2019-07-12 13:02:31 ls a ", "ls a "},
|
||||
{"%F %T ", "2019-07-12 13:02:31 ls a", "ls a"},
|
||||
{"%F %T ", "2019-07-12 13:02:31 ls", "ls"},
|
||||
{"%F %T ", "2019-07-12 13:02:31 ls -Slah", "ls -Slah"},
|
||||
{"%F ", "2019-07-12 ls -Slah", "ls -Slah"},
|
||||
{"%F ", "2019-07-12 ls -Slah", "ls -Slah"},
|
||||
{"%Y", "2019ls -Slah", "ls -Slah"},
|
||||
{"%Y%Y", "20192020ls -Slah", "ls -Slah"},
|
||||
{"%Y%Y", "20192020ls -Slah20192020", "ls -Slah20192020"},
|
||||
{"", "ls -Slah", "ls -Slah"},
|
||||
{"[%F %T] ", "[2019-07-12 13:02:31] sudo apt update", "sudo apt update"},
|
||||
{"[%F a %T] ", "[2019-07-12 a 13:02:31] sudo apt update", "sudo apt update"},
|
||||
{"aaa ", "aaa sudo apt update", "sudo apt update"},
|
||||
{"%c ", "Sun Aug 19 02:56:02 2012 sudo apt update", "sudo apt update"},
|
||||
{"%c ", "Sun Aug 19 02:56:02 2012 ls", "ls"},
|
||||
{"[%c] ", "[Sun Aug 19 02:56:02 2012] ls", "ls"},
|
||||
{"[%c %t] ", "[Sun Aug 19 02:56:02 2012 ] ls", "ls"},
|
||||
{"[%c %t]", "[Sun Aug 19 02:56:02 2012 ]ls", "ls"},
|
||||
{"[%c %t]", "[Sun Aug 19 02:56:02 2012 ]foo", "foo"},
|
||||
{"[%c %t", "[Sun Aug 19 02:56:02 2012 foo", "foo"},
|
||||
{"[%F %T %z]", "[2022-09-28 04:17:06 +0000]foo", "foo"},
|
||||
{"[%F %T %z] ", "[2022-09-28 04:17:06 +0000] foo", "foo"},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
os.Setenv("HISTTIMEFORMAT", tc.env)
|
||||
stripped, err := maybeSkipBashHistTimePrefix(tc.cmdLine)
|
||||
testutils.Check(t, err)
|
||||
if stripped != tc.expected {
|
||||
t.Fatalf("skipping the time prefix returned %#v (expected=%#v for %#v)", stripped, tc.expected, tc.cmdLine)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestChunks(t *testing.T) {
|
||||
testcases := []struct {
|
||||
input []int
|
||||
chunkSize int
|
||||
output [][]int
|
||||
}{
|
||||
{[]int{1, 2, 3, 4, 5}, 2, [][]int{{1, 2}, {3, 4}, {5}}},
|
||||
{[]int{1, 2, 3, 4, 5}, 3, [][]int{{1, 2, 3}, {4, 5}}},
|
||||
{[]int{1, 2, 3, 4, 5}, 1, [][]int{{1}, {2}, {3}, {4}, {5}}},
|
||||
{[]int{1, 2, 3, 4, 5}, 4, [][]int{{1, 2, 3, 4}, {5}}},
|
||||
}
|
||||
for _, tc := range testcases {
|
||||
actual := chunks(tc.input, tc.chunkSize)
|
||||
if !reflect.DeepEqual(actual, tc.output) {
|
||||
t.Fatal("chunks failure")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestZshWeirdness(t *testing.T) {
|
||||
testcases := []struct {
|
||||
input string
|
||||
output string
|
||||
}{
|
||||
{": 1666062975:0;bash", "bash"},
|
||||
{": 16660:0;ls", "ls"},
|
||||
{"ls", "ls"},
|
||||
{"0", "0"},
|
||||
{"hgffddxsdsrzsz xddfgdxfdv gdfc ghcvhgfcfg vgv", "hgffddxsdsrzsz xddfgdxfdv gdfc ghcvhgfcfg vgv"},
|
||||
}
|
||||
for _, tc := range testcases {
|
||||
actual := stripZshWeirdness(tc.input)
|
||||
if !reflect.DeepEqual(actual, tc.output) {
|
||||
t.Fatalf("weirdness failure for %#v", tc.input)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseTimeGenerously(t *testing.T) {
|
||||
ts, err := parseTimeGenerously("2006-01-02T15:04:00-08:00")
|
||||
testutils.Check(t, err)
|
||||
if ts.Unix() != 1136243040 {
|
||||
t.Fatalf("parsed time incorrectly: %d", ts.Unix())
|
||||
}
|
||||
ts, err = parseTimeGenerously("2006-01-02 T15:04:00 -08:00")
|
||||
testutils.Check(t, err)
|
||||
if ts.Unix() != 1136243040 {
|
||||
t.Fatalf("parsed time incorrectly: %d", ts.Unix())
|
||||
}
|
||||
ts, err = parseTimeGenerously("2006-01-02_T15:04:00_-08:00")
|
||||
testutils.Check(t, err)
|
||||
if ts.Unix() != 1136243040 {
|
||||
t.Fatalf("parsed time incorrectly: %d", ts.Unix())
|
||||
}
|
||||
ts, err = parseTimeGenerously("2006-01-02T15:04:00")
|
||||
testutils.Check(t, err)
|
||||
if ts.Year() != 2006 || ts.Month() != time.January || ts.Day() != 2 || ts.Hour() != 15 || ts.Minute() != 4 || ts.Second() != 0 {
|
||||
t.Fatalf("parsed time incorrectly: %d", ts.Unix())
|
||||
}
|
||||
ts, err = parseTimeGenerously("2006-01-02_T15:04:00")
|
||||
testutils.Check(t, err)
|
||||
if ts.Year() != 2006 || ts.Month() != time.January || ts.Day() != 2 || ts.Hour() != 15 || ts.Minute() != 4 || ts.Second() != 0 {
|
||||
t.Fatalf("parsed time incorrectly: %d", ts.Unix())
|
||||
}
|
||||
ts, err = parseTimeGenerously("2006-01-02_15:04:00")
|
||||
testutils.Check(t, err)
|
||||
if ts.Year() != 2006 || ts.Month() != time.January || ts.Day() != 2 || ts.Hour() != 15 || ts.Minute() != 4 || ts.Second() != 0 {
|
||||
t.Fatalf("parsed time incorrectly: %d", ts.Unix())
|
||||
}
|
||||
ts, err = parseTimeGenerously("2006-01-02T15:04")
|
||||
testutils.Check(t, err)
|
||||
if ts.Year() != 2006 || ts.Month() != time.January || ts.Day() != 2 || ts.Hour() != 15 || ts.Minute() != 4 || ts.Second() != 0 {
|
||||
t.Fatalf("parsed time incorrectly: %d", ts.Unix())
|
||||
}
|
||||
ts, err = parseTimeGenerously("2006-01-02_15:04")
|
||||
testutils.Check(t, err)
|
||||
if ts.Year() != 2006 || ts.Month() != time.January || ts.Day() != 2 || ts.Hour() != 15 || ts.Minute() != 4 || ts.Second() != 0 {
|
||||
t.Fatalf("parsed time incorrectly: %d", ts.Unix())
|
||||
}
|
||||
ts, err = parseTimeGenerously("2006-01-02")
|
||||
testutils.Check(t, err)
|
||||
if ts.Year() != 2006 || ts.Month() != time.January || ts.Day() != 2 || ts.Hour() != 0 || ts.Minute() != 0 || ts.Second() != 0 {
|
||||
t.Fatalf("parsed time incorrectly: %d", ts.Unix())
|
||||
}
|
||||
}
|
99
client/lib/slsa.go
Normal file
99
client/lib/slsa.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package lib
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/slsa-framework/slsa-verifier/options"
|
||||
"github.com/slsa-framework/slsa-verifier/verifiers"
|
||||
)
|
||||
|
||||
func verify(ctx *context.Context, provenance []byte, artifactHash, source, branch, versionTag string) error {
|
||||
provenanceOpts := &options.ProvenanceOpts{
|
||||
ExpectedSourceURI: source,
|
||||
ExpectedBranch: &branch,
|
||||
ExpectedDigest: artifactHash,
|
||||
ExpectedVersionedTag: &versionTag,
|
||||
}
|
||||
builderOpts := &options.BuilderOpts{}
|
||||
_, _, err := verifiers.Verify(*ctx, provenance, artifactHash, provenanceOpts, builderOpts)
|
||||
return err
|
||||
}
|
||||
|
||||
func checkForDowngrade(currentVersionS, newVersionS string) error {
|
||||
currentVersion, err := strconv.Atoi(strings.TrimPrefix(currentVersionS, "v0."))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse current version %#v", currentVersionS)
|
||||
}
|
||||
newVersion, err := strconv.Atoi(strings.TrimPrefix(newVersionS, "v0."))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse updated version %#v", newVersionS)
|
||||
}
|
||||
if currentVersion > newVersion {
|
||||
return fmt.Errorf("failed to update because the new version (%#v) is a downgrade compared to the current version (%#v)", newVersionS, currentVersionS)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func verifyBinary(ctx *context.Context, binaryPath, attestationPath, versionTag string) error {
|
||||
if os.Getenv("HISHTORY_DISABLE_SLSA_ATTESTATION") == "true" {
|
||||
return nil
|
||||
}
|
||||
resp, err := ApiGet("/api/v1/slsa-status?newVersion=" + versionTag)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if string(resp) != "OK" {
|
||||
fmt.Printf("SLSA verification is currently broken (%s), skipping SLSA validation...\n", string(resp))
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := checkForDowngrade(Version, versionTag); err != nil && os.Getenv("HISHTORY_ALLOW_DOWNGRADE") == "true" {
|
||||
return err
|
||||
}
|
||||
|
||||
attestation, err := os.ReadFile(attestationPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read attestation file: %v", err)
|
||||
}
|
||||
|
||||
hash, err := getFileHash(binaryPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return verify(ctx, attestation, hash, "github.com/ddworken/hishtory", "master", versionTag)
|
||||
}
|
||||
|
||||
func getFileHash(binaryPath string) (string, error) {
|
||||
binaryFile, err := os.Open(binaryPath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read binary for verification purposes: %v", err)
|
||||
}
|
||||
defer binaryFile.Close()
|
||||
|
||||
hasher := sha256.New()
|
||||
if _, err := io.Copy(hasher, binaryFile); err != nil {
|
||||
return "", fmt.Errorf("failed to hash binary: %v", err)
|
||||
}
|
||||
hash := hex.EncodeToString(hasher.Sum(nil))
|
||||
return hash, nil
|
||||
}
|
||||
|
||||
func handleSlsaFailure(srcErr error) error {
|
||||
fmt.Printf("\nFailed to verify SLSA provenance! This is likely due to a SLSA bug (SLSA is a brand new standard, and like all new things, has bugs). Ignoring this failure means falling back to the way most software does updates. Do you want to ignore this failure and update anyways? [y/N]")
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
resp, err := reader.ReadString('\n')
|
||||
if err == nil && strings.TrimSpace(resp) == "y" {
|
||||
fmt.Println("Proceeding with update...")
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("failed to verify SLSA provenance of the updated binary, aborting update (to bypass, set `export HISHTORY_DISABLE_SLSA_ATTESTATION=true`): %v", srcErr)
|
||||
}
|
451
client/lib/tui.go
Normal file
451
client/lib/tui.go
Normal file
@@ -0,0 +1,451 @@
|
||||
package lib
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
_ "embed" // for embedding config.sh
|
||||
|
||||
"github.com/charmbracelet/bubbles/key"
|
||||
"github.com/charmbracelet/bubbles/spinner"
|
||||
"github.com/charmbracelet/bubbles/table"
|
||||
"github.com/charmbracelet/bubbles/textinput"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/ddworken/hishtory/client/hctx"
|
||||
"github.com/muesli/termenv"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
const TABLE_HEIGHT = 20
|
||||
const PADDED_NUM_ENTRIES = TABLE_HEIGHT * 5
|
||||
|
||||
var selectedRow string = ""
|
||||
|
||||
var baseStyle = lipgloss.NewStyle().
|
||||
BorderStyle(lipgloss.NormalBorder()).
|
||||
BorderForeground(lipgloss.Color("240"))
|
||||
|
||||
type errMsg error
|
||||
|
||||
type model struct {
|
||||
// context
|
||||
ctx *context.Context
|
||||
|
||||
// Model for the loading spinner.
|
||||
spinner spinner.Model
|
||||
// Whether data is still loading and the spinner should still be displayed.
|
||||
isLoading bool
|
||||
|
||||
// Whether the TUI is quitting.
|
||||
quitting bool
|
||||
|
||||
// The table used for displaying search results.
|
||||
table table.Model
|
||||
// The number of entries in the table.
|
||||
numEntries int
|
||||
// Whether the user has hit enter to select an entry and the TUI is thus about to quit.
|
||||
selected bool
|
||||
|
||||
// The search box for the query
|
||||
queryInput textinput.Model
|
||||
// The query to run. Reset to nil after it was run.
|
||||
runQuery *string
|
||||
// The previous query that was run.
|
||||
lastQuery string
|
||||
|
||||
// Unrecoverable error.
|
||||
err error
|
||||
// An error while searching. Recoverable and displayed as a warning message.
|
||||
searchErr error
|
||||
// Whether the device is offline. If so, a warning will be displayed.
|
||||
isOffline bool
|
||||
|
||||
// A banner from the backend to be displayed. Generally an empty string.
|
||||
banner string
|
||||
}
|
||||
|
||||
type doneDownloadingMsg struct{}
|
||||
type offlineMsg struct{}
|
||||
type bannerMsg struct {
|
||||
banner string
|
||||
}
|
||||
|
||||
func initialModel(ctx *context.Context, t table.Model, initialQuery string, numEntries int) model {
|
||||
s := spinner.New()
|
||||
s.Spinner = spinner.Dot
|
||||
s.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("205"))
|
||||
queryInput := textinput.New()
|
||||
queryInput.Placeholder = "ls"
|
||||
queryInput.Focus()
|
||||
queryInput.CharLimit = 156
|
||||
queryInput.Width = 50
|
||||
if initialQuery != "" {
|
||||
queryInput.SetValue(initialQuery)
|
||||
}
|
||||
return model{ctx: ctx, spinner: s, isLoading: true, table: t, runQuery: &initialQuery, queryInput: queryInput, numEntries: numEntries}
|
||||
}
|
||||
|
||||
func (m model) Init() tea.Cmd {
|
||||
return m.spinner.Tick
|
||||
}
|
||||
|
||||
func runQueryAndUpdateTable(m model, updateTable bool) model {
|
||||
if (m.runQuery != nil && *m.runQuery != m.lastQuery) || updateTable {
|
||||
if m.runQuery == nil {
|
||||
m.runQuery = &m.lastQuery
|
||||
}
|
||||
rows, numEntries, err := getRows(m.ctx, hctx.GetConf(m.ctx).DisplayedColumns, *m.runQuery, PADDED_NUM_ENTRIES)
|
||||
if err != nil {
|
||||
m.searchErr = err
|
||||
return m
|
||||
} else {
|
||||
m.searchErr = nil
|
||||
}
|
||||
m.numEntries = numEntries
|
||||
if updateTable {
|
||||
t, err := makeTable(m.ctx, rows)
|
||||
if err != nil {
|
||||
m.err = err
|
||||
return m
|
||||
}
|
||||
m.table = t
|
||||
}
|
||||
m.table.SetRows(rows)
|
||||
m.table.SetCursor(0)
|
||||
m.lastQuery = *m.runQuery
|
||||
m.runQuery = nil
|
||||
}
|
||||
if m.table.Cursor() >= m.numEntries {
|
||||
// Ensure that we can't scroll past the end of the table
|
||||
m.table.SetCursor(m.numEntries - 1)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
switch msg.String() {
|
||||
case "esc", "ctrl+c":
|
||||
m.quitting = true
|
||||
return m, tea.Quit
|
||||
case "enter":
|
||||
if m.numEntries != 0 {
|
||||
m.selected = true
|
||||
}
|
||||
return m, tea.Quit
|
||||
default:
|
||||
t, cmd1 := m.table.Update(msg)
|
||||
m.table = t
|
||||
if strings.HasPrefix(msg.String(), "alt+") {
|
||||
return m, tea.Batch(cmd1)
|
||||
}
|
||||
i, cmd2 := m.queryInput.Update(msg)
|
||||
m.queryInput = i
|
||||
searchQuery := m.queryInput.Value()
|
||||
m.runQuery = &searchQuery
|
||||
m = runQueryAndUpdateTable(m, false)
|
||||
return m, tea.Batch(cmd1, cmd2)
|
||||
}
|
||||
case tea.WindowSizeMsg:
|
||||
m = runQueryAndUpdateTable(m, true)
|
||||
return m, nil
|
||||
case errMsg:
|
||||
m.err = msg
|
||||
return m, nil
|
||||
case offlineMsg:
|
||||
m.isOffline = true
|
||||
return m, nil
|
||||
case bannerMsg:
|
||||
m.banner = msg.banner
|
||||
return m, nil
|
||||
case doneDownloadingMsg:
|
||||
m.isLoading = false
|
||||
return m, nil
|
||||
default:
|
||||
var cmd tea.Cmd
|
||||
if m.isLoading {
|
||||
m.spinner, cmd = m.spinner.Update(msg)
|
||||
return m, cmd
|
||||
} else {
|
||||
m.table, cmd = m.table.Update(msg)
|
||||
return m, cmd
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m model) View() string {
|
||||
if m.err != nil {
|
||||
return fmt.Sprintf("An unrecoverable error occured: %v\n", m.err)
|
||||
}
|
||||
if m.selected {
|
||||
indexOfCommand := -1
|
||||
for i, columnName := range hctx.GetConf(m.ctx).DisplayedColumns {
|
||||
if columnName == "Command" {
|
||||
indexOfCommand = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if indexOfCommand == -1 {
|
||||
selectedRow = "Error: Table doesn't have a column named `Command`?"
|
||||
return ""
|
||||
}
|
||||
selectedRow = m.table.SelectedRow()[indexOfCommand]
|
||||
return ""
|
||||
}
|
||||
if m.quitting {
|
||||
return ""
|
||||
}
|
||||
loadingMessage := ""
|
||||
if m.isLoading {
|
||||
loadingMessage = fmt.Sprintf("%s Loading hishtory entries from other devices...", m.spinner.View())
|
||||
}
|
||||
warning := ""
|
||||
if m.isOffline {
|
||||
warning += "Warning: failed to contact the hishtory backend (are you offline?), so some results may be stale\n\n"
|
||||
}
|
||||
if m.searchErr != nil {
|
||||
warning += fmt.Sprintf("Warning: failed to search: %v\n\n", m.searchErr)
|
||||
}
|
||||
return fmt.Sprintf("\n%s\n%s%s\nSearch Query: %s\n\n%s\n", loadingMessage, warning, m.banner, m.queryInput.View(), baseStyle.Render(m.table.View()))
|
||||
}
|
||||
|
||||
func getRows(ctx *context.Context, columnNames []string, query string, numEntries int) ([]table.Row, int, error) {
|
||||
db := hctx.GetDb(ctx)
|
||||
config := hctx.GetConf(ctx)
|
||||
data, err := Search(ctx, db, query, numEntries)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
var rows []table.Row
|
||||
lastCommand := ""
|
||||
for i := 0; i < numEntries; i++ {
|
||||
if i < len(data) {
|
||||
entry := data[i]
|
||||
if strings.TrimSpace(entry.Command) == strings.TrimSpace(lastCommand) && config.FilterDuplicateCommands {
|
||||
continue
|
||||
}
|
||||
entry.Command = strings.ReplaceAll(entry.Command, "\n", " ") // TODO: handle multi-line commands better here
|
||||
row, err := buildTableRow(ctx, columnNames, *entry)
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("failed to build row for entry=%#v: %v", entry, err)
|
||||
}
|
||||
rows = append(rows, row)
|
||||
lastCommand = entry.Command
|
||||
} else {
|
||||
rows = append(rows, table.Row{})
|
||||
}
|
||||
}
|
||||
return rows, len(data), nil
|
||||
}
|
||||
|
||||
func calculateColumnWidths(rows []table.Row) []int {
|
||||
numColumns := len(rows[0])
|
||||
neededColumnWidth := make([]int, numColumns)
|
||||
for _, row := range rows {
|
||||
for i, v := range row {
|
||||
neededColumnWidth[i] = max(neededColumnWidth[i], len(v))
|
||||
}
|
||||
}
|
||||
return neededColumnWidth
|
||||
}
|
||||
|
||||
func getTerminalSize() (int, int, error) {
|
||||
return term.GetSize(2)
|
||||
}
|
||||
|
||||
var bigQueryResults []table.Row
|
||||
|
||||
func makeTableColumns(ctx *context.Context, columnNames []string, rows []table.Row) ([]table.Column, error) {
|
||||
// Handle an initial query with no results
|
||||
if len(rows) == 0 || len(rows[0]) == 0 {
|
||||
allRows, _, err := getRows(ctx, columnNames, "", 25)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return makeTableColumns(ctx, columnNames, allRows)
|
||||
}
|
||||
|
||||
// Calculate the minimum amount of space that we need for each column for the current actual search
|
||||
columnWidths := calculateColumnWidths(rows)
|
||||
totalWidth := 20
|
||||
for i, name := range columnNames {
|
||||
columnWidths[i] = max(columnWidths[i], len(name))
|
||||
totalWidth += columnWidths[i]
|
||||
}
|
||||
|
||||
// Calculate the maximum column width that is useful for each column if we search for the empty string
|
||||
if bigQueryResults == nil {
|
||||
bigRows, _, err := getRows(ctx, columnNames, "", 1000)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bigQueryResults = bigRows
|
||||
}
|
||||
maximumColumnWidths := calculateColumnWidths(bigQueryResults)
|
||||
|
||||
// Get the actual terminal width. If we're below this, opportunistically add some padding aiming for the maximum column widths
|
||||
terminalWidth, _, err := getTerminalSize()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get terminal size: %v", err)
|
||||
}
|
||||
for totalWidth < (terminalWidth - len(columnNames)) {
|
||||
prevTotalWidth := totalWidth
|
||||
for i := range columnNames {
|
||||
if columnWidths[i] < maximumColumnWidths[i]+5 {
|
||||
columnWidths[i] += 1
|
||||
totalWidth += 1
|
||||
}
|
||||
}
|
||||
if totalWidth == prevTotalWidth {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// And if we are too large from the initial query, let's shrink things to make the table fit. We'll use the heuristic of always shrinking the widest column.
|
||||
for totalWidth > terminalWidth {
|
||||
largestColumnIdx := -1
|
||||
largestColumnSize := -1
|
||||
for i := range columnNames {
|
||||
if columnWidths[i] > largestColumnSize {
|
||||
largestColumnIdx = i
|
||||
largestColumnSize = columnWidths[i]
|
||||
}
|
||||
}
|
||||
columnWidths[largestColumnIdx] -= 2
|
||||
totalWidth -= 2
|
||||
}
|
||||
|
||||
// And finally, create some actual columns!
|
||||
columns := make([]table.Column, 0)
|
||||
for i, name := range columnNames {
|
||||
columns = append(columns, table.Column{Title: name, Width: columnWidths[i]})
|
||||
}
|
||||
return columns, nil
|
||||
}
|
||||
|
||||
func max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func makeTable(ctx *context.Context, rows []table.Row) (table.Model, error) {
|
||||
config := hctx.GetConf(ctx)
|
||||
columns, err := makeTableColumns(ctx, config.DisplayedColumns, rows)
|
||||
if err != nil {
|
||||
return table.Model{}, err
|
||||
}
|
||||
km := table.KeyMap{
|
||||
LineUp: key.NewBinding(
|
||||
key.WithKeys("up", "alt+OA"),
|
||||
key.WithHelp("↑", "scroll up"),
|
||||
),
|
||||
LineDown: key.NewBinding(
|
||||
key.WithKeys("down", "alt+OB"),
|
||||
key.WithHelp("↓", "scroll down"),
|
||||
),
|
||||
PageUp: key.NewBinding(
|
||||
key.WithKeys("pgup"),
|
||||
key.WithHelp("pgup", "page up"),
|
||||
),
|
||||
PageDown: key.NewBinding(
|
||||
key.WithKeys("pgdown"),
|
||||
key.WithHelp("pgdn", "page down"),
|
||||
),
|
||||
GotoTop: key.NewBinding(
|
||||
key.WithKeys("home"),
|
||||
key.WithHelp("home", "go to start"),
|
||||
),
|
||||
GotoBottom: key.NewBinding(
|
||||
key.WithKeys("end"),
|
||||
key.WithHelp("end", "go to end"),
|
||||
),
|
||||
}
|
||||
_, terminalHeight, err := getTerminalSize()
|
||||
if err != nil {
|
||||
return table.Model{}, err
|
||||
}
|
||||
tableHeight := min(TABLE_HEIGHT, terminalHeight-12)
|
||||
t := table.New(
|
||||
table.WithColumns(columns),
|
||||
table.WithRows(rows),
|
||||
table.WithFocused(true),
|
||||
table.WithHeight(tableHeight),
|
||||
table.WithKeyMap(km),
|
||||
)
|
||||
|
||||
s := table.DefaultStyles()
|
||||
s.Header = s.Header.
|
||||
BorderStyle(lipgloss.NormalBorder()).
|
||||
BorderForeground(lipgloss.Color("240")).
|
||||
BorderBottom(true).
|
||||
Bold(false)
|
||||
s.Selected = s.Selected.
|
||||
Foreground(lipgloss.Color("229")).
|
||||
Background(lipgloss.Color("57")).
|
||||
Bold(false)
|
||||
t.SetStyles(s)
|
||||
t.Focus()
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func TuiQuery(ctx *context.Context, gitCommit, initialQuery string) error {
|
||||
lipgloss.SetColorProfile(termenv.ANSI)
|
||||
rows, numEntries, err := getRows(ctx, hctx.GetConf(ctx).DisplayedColumns, initialQuery, PADDED_NUM_ENTRIES)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t, err := makeTable(ctx, rows)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p := tea.NewProgram(initialModel(ctx, t, initialQuery, numEntries), tea.WithOutput(os.Stderr))
|
||||
go func() {
|
||||
err := RetrieveAdditionalEntriesFromRemote(ctx)
|
||||
if err != nil {
|
||||
p.Send(err)
|
||||
}
|
||||
p.Send(doneDownloadingMsg{})
|
||||
}()
|
||||
// Async: Process deletion requests
|
||||
go func() {
|
||||
err := ProcessDeletionRequests(ctx)
|
||||
if err != nil {
|
||||
p.Send(err)
|
||||
}
|
||||
}()
|
||||
// Async: Check for any banner from the server
|
||||
go func() {
|
||||
banner, err := GetBanner(ctx, gitCommit)
|
||||
if err != nil {
|
||||
if IsOfflineError(err) {
|
||||
p.Send(offlineMsg{})
|
||||
} else {
|
||||
p.Send(err)
|
||||
}
|
||||
}
|
||||
p.Send(bannerMsg{banner: string(banner)})
|
||||
}()
|
||||
// Blocking: Start the TUI
|
||||
err = p.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if selectedRow == "" && os.Getenv("HISHTORY_TERM_INTEGRATION") != "" {
|
||||
// Print out the initialQuery instead so that we don't clear the terminal
|
||||
selectedRow = initialQuery
|
||||
}
|
||||
fmt.Printf("%s\n", selectedRow)
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user