├── .dockerignore ├── .errcheck_excludes.txt ├── .github ├── push_event.json ├── slsa │ ├── .slsa-goreleaser-darwin-amd64.yml │ ├── .slsa-goreleaser-darwin-arm64.yml │ ├── .slsa-goreleaser-freebsd-amd64.yml │ ├── .slsa-goreleaser-linux-amd64.yml │ ├── .slsa-goreleaser-linux-arm64.yml │ ├── .slsa-goreleaser-linux-arm7.yml │ └── .slsa-goreleaser-netbsd-amd64.yml └── workflows │ ├── codeql-analysis.yml │ ├── distro-smoke-test.yml │ ├── docker-compose-test.yml │ ├── go-test.yml │ ├── pre-commit.yml │ ├── server-releaser.yml │ └── slsa-releaser.yml ├── .gitignore ├── .pre-commit-config.yaml ├── LICENSE ├── Makefile ├── README.md ├── VERSION ├── backend ├── server │ ├── Dockerfile │ ├── docker-compose.yml │ ├── internal │ │ ├── database │ │ │ ├── db.go │ │ │ ├── device.go │ │ │ ├── historyentries.go │ │ │ └── usagedata.go │ │ ├── release │ │ │ ├── release.go │ │ │ └── release_test.go │ │ └── server │ │ │ ├── api_handlers.go │ │ │ ├── debug_handlers.go │ │ │ ├── middleware.go │ │ │ ├── middleware_test.go │ │ │ ├── server_test.go │ │ │ ├── srv.go │ │ │ └── util.go │ └── server.go └── web │ ├── caddy │ ├── Caddyfile │ └── Dockerfile │ └── landing │ └── www │ ├── .gitignore │ ├── img │ ├── aidemo.png │ ├── demo.gif │ └── webui.png │ ├── index.html │ └── install.py ├── client ├── ai │ └── ai.go ├── cmd │ ├── configAdd.go │ ├── configDelete.go │ ├── configGet.go │ ├── configKeyBindings.go │ ├── configSet.go │ ├── enableDisable.go │ ├── export.go │ ├── import.go │ ├── install.go │ ├── install_test.go │ ├── query.go │ ├── redact.go │ ├── reupload.go │ ├── root.go │ ├── saveHistoryEntry.go │ ├── saveHistoryEntry_test.go │ ├── status.go │ ├── syncing.go │ ├── update.go │ └── webui.go ├── data │ ├── data.go │ └── data_test.go ├── fuzz_test.go ├── hctx │ └── hctx.go ├── integration_test.go ├── lib │ ├── config.fish │ ├── config.sh │ ├── config.zsh │ ├── lib.go │ ├── lib_test.go │ ├── net.go │ ├── net_disabled.go │ ├── slsa.go │ └── slsa_test.go ├── posttest │ └── main.go ├── table │ ├── table.go │ └── table_test.go ├── testdata │ ├── TestBashOrderingBug-Export │ ├── TestChangeSyncingStatus-Offline │ ├── TestChangeSyncingStatus-Online │ ├── TestDefaultSearchColumns-Default-Echo │ ├── TestDefaultSearchColumns-Default-Hi │ ├── TestDefaultSearchColumns-MyCol-bar │ ├── TestDefaultSearchColumns-MyCol-baz │ ├── TestDefaultSearchColumns-NoCWD-Echo │ ├── TestDefaultSearchColumns-NoCWD-Hi │ ├── TestDefaultSearchColumns-NoCWDHostname-Echo │ ├── TestDefaultSearchColumns-NoCWDHostname-Hi │ ├── TestExportJson │ ├── TestFish-table │ ├── TestImportHistory-export │ ├── TestInstallSkipConfigModification-InstallOutput-darwin │ ├── TestInstallSkipConfigModification-InstallOutput-linux │ ├── TestSortByConsistentTimezone-export │ ├── TestSortByConsistentTimezone-query │ ├── TestStatusFullConfig │ ├── TestTimestampFormat-query │ ├── TestTimestampFormat-tquery │ ├── TestTui-AiQuery │ ├── TestTui-AiQuery-Disabled │ ├── TestTui-ColoredOutput-darwin-23 │ ├── TestTui-ColoredOutput-linux-actions │ ├── TestTui-ColoredOutputWithCustomColorScheme-darwin-23 │ ├── TestTui-ColoredOutputWithCustomColorScheme-linux-actions │ ├── TestTui-ColoredOutputWithDefaultFilter-darwin-23 │ ├── TestTui-ColoredOutputWithDefaultFilter-linux-actions │ ├── TestTui-ColoredOutputWithSearch-Highlight-darwin-23 │ ├── TestTui-ColoredOutputWithSearch-Highlight-linux-actions │ ├── TestTui-ColoredOutputWithSearch-darwin-23 │ ├── TestTui-ColoredOutputWithSearch-linux-actions │ ├── TestTui-DefaultColorScheme │ ├── TestTui-DefaultFilter-Deleted │ ├── TestTui-DefaultFilter-DeletedWithText │ ├── TestTui-DefaultFilter-Enabled │ ├── TestTui-DefaultFilter-EnabledAdditionalQuery │ ├── TestTui-Delete │ ├── TestTui-DeleteAgain │ ├── TestTui-DeleteAgainStill │ ├── TestTui-DeleteStill │ ├── TestTui-Escaping │ ├── TestTui-Exit-darwin │ ├── TestTui-Exit-linux │ ├── TestTui-ExportWithAdditionalEntries │ ├── TestTui-ExportWithEvenMoreEntries │ ├── TestTui-ForcedCompactMode │ ├── TestTui-FullScreenCompactRender │ ├── TestTui-FullScreenHelp │ ├── TestTui-FullScreenRender │ ├── TestTui-HelpPage │ ├── TestTui-HelpPageClosed │ ├── TestTui-Initial │ ├── TestTui-InitialInvalidSearch │ ├── TestTui-InvalidSearch │ ├── TestTui-InvalidSearchBecomesValid │ ├── TestTui-JumpCursor │ ├── TestTui-KeyBindings-Configured │ ├── TestTui-KeyBindings-Default │ ├── TestTui-KeyBindings-Help │ ├── TestTui-LeftScroll │ ├── TestTui-LongDirectoryName │ ├── TestTui-LongQuery │ ├── TestTui-Offline │ ├── TestTui-OfflineInvalid │ ├── TestTui-Resize │ ├── TestTui-RightScroll │ ├── TestTui-RightScrollDirectoryTwo │ ├── TestTui-RightScrollTwo │ ├── TestTui-Search │ ├── TestTui-SearchBackslash │ ├── TestTui-SearchColonDoubleQuoted │ ├── TestTui-SearchColonError │ ├── TestTui-SearchColonEscaped │ ├── TestTui-SearchQuoteDash │ ├── TestTui-SearchQuoted │ ├── TestTui-SearchUnquoted │ ├── TestTui-SelectAndCd │ ├── TestTui-SmallTerminal │ ├── TestTui-TiniestTerminal │ ├── TestTui-TinyTerminal │ ├── TestTui-TinyTerminalHelp │ ├── TestTuiBench-Query │ ├── testControlR-AdvancedSearch │ ├── testControlR-ControlC-bash-darwin │ ├── testControlR-ControlC-bash-linux │ ├── testControlR-ControlC-zsh-darwin │ ├── testControlR-ControlC-zsh-linux │ ├── testControlR-DisplayMultiline-bash │ ├── testControlR-DisplayMultiline-fish │ ├── testControlR-DisplayMultiline-zsh │ ├── testControlR-Final │ ├── testControlR-Initial │ ├── testControlR-InitialExport │ ├── testControlR-InitialSearch │ ├── testControlR-InitialSearchExpanded │ ├── testControlR-InitialSearchNoResults │ ├── testControlR-InitialSearchNoResultsThenFoundResults │ ├── testControlR-Search │ ├── testControlR-SelectMultiline-bash-darwin │ ├── testControlR-SelectMultiline-bash-linux │ ├── testControlR-SelectMultiline-zsh-darwin │ ├── testControlR-SelectMultiline-zsh-linux │ ├── testControlR-bash-Disabled-darwin │ ├── testControlR-bash-Disabled-linux │ ├── testControlR-customColumn │ ├── testControlR-displayedColumns │ ├── testControlR-zsh-Disabled-darwin │ ├── testControlR-zsh-Disabled-linux │ ├── testCustomColumns-initHistory │ ├── testCustomColumns-query-isAction=false │ ├── testCustomColumns-query-isAction=true │ ├── testCustomColumns-tquery-bash │ ├── testCustomColumns-tquery-bash-isAction │ ├── testCustomColumns-tquery-zsh │ ├── testCustomColumns-tquery-zsh-isAction │ ├── testDisplayTable-customColumns │ ├── testDisplayTable-customColumns-2 │ ├── testDisplayTable-customColumns-3 │ ├── testDisplayTable-customColumns-multiLineCommand │ ├── testDisplayTable-customColumns-trulyCustom │ ├── testDisplayTable-defaultColumns │ ├── testIntegrationWithNewDevice-bash │ ├── testIntegrationWithNewDevice-tablebash │ ├── testIntegrationWithNewDevice-tablezsh │ ├── testIntegrationWithNewDevice-zsh │ ├── testPresaving-query │ ├── testPresavingOffline-query-missing │ ├── testPresavingOffline-query-present │ ├── testRemoveDuplicateRows-enabled-export │ ├── testRemoveDuplicateRows-enabled-query │ ├── testRemoveDuplicateRows-enabled-tquery │ ├── testRemoveDuplicateRows-export │ ├── testRemoveDuplicateRows-query │ ├── testRemoveDuplicateRows-tquery │ ├── testTabCompletion-suggestions-fish │ ├── testTabCompletion-suggestions-zsh │ ├── testUninstall-post-uninstall │ ├── testUninstall-post-uninstall-bash-darwin │ ├── testUninstall-post-uninstall-bash-linux │ ├── testUninstall-post-uninstall-zsh-darwin │ ├── testUninstall-post-uninstall-zsh-linux │ ├── testUninstall-recorded │ ├── testUninstall-uninstall-bash │ ├── testUninstall-uninstall-zsh │ ├── unittestTable-truncatedTable │ ├── unittestTable-truncatedTable-right1 │ ├── unittestTable-truncatedTable-right2 │ └── unittestTable-truncatedTable-right3 ├── testutils.go ├── tui │ ├── keybindings │ │ └── keybindings.go │ ├── tui.go │ └── tui_test.go └── webui │ ├── templates │ └── webui.html │ └── webui.go ├── demo.vhs ├── docs └── offline-binary.md ├── go.mod ├── go.sum ├── hishtory.go ├── scripts ├── actions-sign.py ├── actions-validate.py └── client-ldflags └── shared ├── ai ├── ai.go └── ai_test.go ├── data.go ├── testutils └── testutils.go ├── time.go ├── utils.go ├── version.go └── version_test.go /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /.errcheck_excludes.txt: -------------------------------------------------------------------------------- 1 | (net/http.ResponseWriter).Write 2 | (*gorm.io/gorm.DB).AutoMigrate 3 | os.Setenv 4 | os.Unsetenv 5 | (*os.File).Close 6 | (io.ReadCloser).Close 7 | (*github.com/gofrs/flock.Flock).Unlock 8 | (*database/sql.Rows).Close 9 | (*github.com/DataDog/datadog-go/statsd.Client).Count 10 | (*github.com/DataDog/datadog-go/statsd.Client).Incr 11 | (*github.com/DataDog/datadog-go/statsd.Client).Distribution 12 | (*github.com/schollz/progressbar/v3.ProgressBar).Finish 13 | (*github.com/DataDog/datadog-go/statsd.Client).Close 14 | -------------------------------------------------------------------------------- /.github/push_event.json: -------------------------------------------------------------------------------- 1 | { 2 | "push": { 3 | "head": { 4 | "ref": "users/foo/update-action" 5 | }, 6 | "base": { 7 | "ref": "users/foo/update-action" 8 | } 9 | }, 10 | "head_commit": { 11 | "message": "build latest" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.github/slsa/.slsa-goreleaser-darwin-amd64.yml: -------------------------------------------------------------------------------- 1 | version: 1 2 | 3 | env: 4 | - CGO_ENABLED=0 5 | 6 | flags: 7 | - -trimpath 8 | 9 | goos: darwin 10 | goarch: amd64 11 | 12 | binary: hishtory-{{ .Os }}-{{ .Arch }} 13 | 14 | ldflags: 15 | - '{{ .Env.VERSION_LDFLAGS }}' 16 | -------------------------------------------------------------------------------- /.github/slsa/.slsa-goreleaser-darwin-arm64.yml: -------------------------------------------------------------------------------- 1 | version: 1 2 | 3 | env: 4 | - CGO_ENABLED=0 5 | 6 | flags: 7 | - -trimpath 8 | 9 | goos: darwin 10 | goarch: arm64 11 | 12 | binary: hishtory-{{ .Os }}-{{ .Arch }} 13 | 14 | ldflags: 15 | - '{{ .Env.VERSION_LDFLAGS }}' 16 | -------------------------------------------------------------------------------- /.github/slsa/.slsa-goreleaser-freebsd-amd64.yml: -------------------------------------------------------------------------------- 1 | version: 1 2 | 3 | env: 4 | - CGO_ENABLED=0 5 | 6 | flags: 7 | - -trimpath 8 | 9 | goos: freebsd 10 | goarch: amd64 11 | 12 | binary: hishtory-{{ .Os }}-{{ .Arch }} 13 | 14 | ldflags: 15 | - '{{ .Env.VERSION_LDFLAGS }}' 16 | -------------------------------------------------------------------------------- /.github/slsa/.slsa-goreleaser-linux-amd64.yml: -------------------------------------------------------------------------------- 1 | version: 1 2 | 3 | env: 4 | - CGO_ENABLED=0 5 | 6 | flags: 7 | - -trimpath 8 | 9 | goos: linux 10 | goarch: amd64 11 | 12 | binary: hishtory-{{ .Os }}-{{ .Arch }} 13 | 14 | ldflags: 15 | - '{{ .Env.VERSION_LDFLAGS }}' 16 | -------------------------------------------------------------------------------- /.github/slsa/.slsa-goreleaser-linux-arm64.yml: -------------------------------------------------------------------------------- 1 | version: 1 2 | 3 | env: 4 | - CGO_ENABLED=0 5 | 6 | flags: 7 | - -trimpath 8 | 9 | goos: linux 10 | goarch: arm64 11 | 12 | binary: hishtory-{{ .Os }}-{{ .Arch }} 13 | 14 | ldflags: 15 | - '{{ .Env.VERSION_LDFLAGS }}' 16 | -------------------------------------------------------------------------------- /.github/slsa/.slsa-goreleaser-linux-arm7.yml: -------------------------------------------------------------------------------- 1 | version: 1 2 | 3 | env: 4 | - CGO_ENABLED=0 5 | 6 | flags: 7 | - -trimpath 8 | 9 | goos: linux 10 | goarch: arm 11 | 12 | binary: hishtory-{{ .Os }}-{{ .Arch }} 13 | 14 | ldflags: 15 | - '{{ .Env.VERSION_LDFLAGS }}' 16 | -------------------------------------------------------------------------------- /.github/slsa/.slsa-goreleaser-netbsd-amd64.yml: -------------------------------------------------------------------------------- 1 | version: 1 2 | 3 | env: 4 | - CGO_ENABLED=0 5 | 6 | flags: 7 | - -trimpath 8 | 9 | goos: netbsd 10 | goarch: amd64 11 | 12 | binary: hishtory-{{ .Os }}-{{ .Arch }} 13 | 14 | ldflags: 15 | - '{{ .Env.VERSION_LDFLAGS }}' 16 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | # The branches below must be a subset of the branches above 8 | branches: [ master ] 9 | schedule: 10 | - cron: '16 13 * * 4' 11 | 12 | jobs: 13 | analyze: 14 | name: Analyze 15 | runs-on: ubuntu-latest 16 | permissions: 17 | actions: read 18 | contents: read 19 | security-events: write 20 | 21 | strategy: 22 | fail-fast: false 23 | matrix: 24 | language: [ 'go' ] 25 | 26 | steps: 27 | - name: Checkout repository 28 | uses: actions/checkout@v3 29 | - name: Initialize CodeQL 30 | uses: github/codeql-action/init@v2 31 | with: 32 | languages: ${{ matrix.language }} 33 | - name: Autobuild 34 | uses: github/codeql-action/autobuild@v2 35 | - name: Perform CodeQL Analysis 36 | if: ${{ !startsWith(github.event.head_commit.message, 'Release') }} 37 | uses: github/codeql-action/analyze@v2 38 | -------------------------------------------------------------------------------- /.github/workflows/distro-smoke-test.yml: -------------------------------------------------------------------------------- 1 | name: Smoke Test 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | schedule: 7 | - cron: '0 0 * * *' 8 | push: 9 | branches: [ master ] 10 | 11 | jobs: 12 | test: 13 | strategy: 14 | matrix: 15 | distro: ['ubuntu:latest', 'fedora:latest', 'debian:latest', 'archlinux:latest'] 16 | fail-fast: false 17 | runs-on: ubuntu-latest 18 | container: ${{ matrix.distro }} 19 | steps: 20 | - name: Debian-based Setup 21 | if: ${{ matrix.distro == 'ubuntu:latest' || matrix.distro == 'debian:latest' }} 22 | run: | 23 | 24 | # Install our dependencies 25 | apt-get update 26 | apt-get install -y zsh tmux fish ca-certificates make build-essential gcc psmisc 27 | 28 | # Work around a weird bug where zsh on ubuntu actions gives that directory 0777 which makes zsh refuse to start 29 | chmod 0755 -R /usr/share/zsh/ 30 | - name: DNF-based Setup 31 | if: ${{ matrix.distro == 'fedora:latest' || matrix.distro == 'rockylinux:latest' }} 32 | run: | 33 | 34 | # Install our dependencies 35 | sudo dnf update -y 36 | sudo dnf install -y zsh tmux fish make gcc psmisc 37 | - name: Arch-based Setup 38 | if: ${{ matrix.distro == 'archlinux:latest' }} 39 | run: | 40 | 41 | # Install our dependencies 42 | pacman -Sy --noconfirm zsh tmux fish make gcc psmisc python-pip openssl python-pyopenssl 43 | - uses: actions/checkout@v4 44 | - name: Set up Go 45 | uses: actions/setup-go@v4 46 | with: 47 | go-version: 1.23 48 | - name: Go test 49 | if: ${{ !startsWith(github.event.head_commit.message, 'Release') }} 50 | env: 51 | DD_API_KEY: ${{ secrets.DD_API_KEY }} 52 | OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} 53 | run: | 54 | go install gotest.tools/gotestsum@bc98120 55 | make ftest FILTER=TestInstallViaPythonScriptFromHead 56 | # - name: Setup tmate session 57 | # if: ${{ failure() }} 58 | # uses: mxschmitt/action-tmate@v3 59 | # with: 60 | # limit-access-to-actor: true 61 | -------------------------------------------------------------------------------- /.github/workflows/docker-compose-test.yml: -------------------------------------------------------------------------------- 1 | name: Self-Hosting Docker Compose Tests 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | push: 7 | branches: [ master ] 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | - name: Set up Go 15 | uses: actions/setup-go@v4 16 | with: 17 | go-version: 1.23 18 | - name: Docker Compose setup 19 | run: | 20 | set -euo pipefail 21 | sudo apt-get update 22 | sudo apt-get install -y zsh fish 23 | curl -fsSL https://get.docker.com | sudo sh 24 | sudo chmod 0755 -R /usr/share/zsh/ # Work around a weird bug where zsh on ubuntu actions gives that diretory 0777 which makes zsh refuse to start 25 | sudo hostname ghaction-runner-hostname # Set a consistent hostname so we can run tests that depend on it 26 | docker compose -f backend/server/docker-compose.yml build 27 | HISHTORY_COMPOSE_TEST=1 docker compose -f backend/server/docker-compose.yml up -d 28 | export HISHTORY_SERVER=http://localhost 29 | go build 30 | ./hishtory install 31 | - name: Docker Compose test 32 | shell: bash -il {0} 33 | run: | 34 | set -eo pipefail 35 | export HISHTORY_SERVER=http://localhost 36 | source ~/.bashrc 37 | # Check that hishtory query runs without errors 38 | ./hishtory query 39 | # Run a command such that hishtory will record it 40 | echo -e 'ls -Slah /\n' | zsh -is 41 | # Sleep to ensure there is time for it to be recorded, since recordings are async 42 | sleep 1 43 | # Check that it was recorded 44 | ./hishtory export | grep "ls -Slah /" 45 | # Check that we can redact it 46 | HISHTORY_REDACT_FORCE=1 ./hishtory redact ls Slah 47 | # And that it was redacted 48 | ! (./hishtory export | grep "ls -Slah /") 49 | # Assert that the entry is syncing properly 50 | ./hishtory status -v | grep 'Sync Status: Synced' 51 | # And that we are properly using the self-hosted server 52 | ./hishtory status -v | grep 'Sync Server: http://localhost' 53 | # Show the full status output for debugging 54 | ./hishtory status -v 55 | # - name: Setup tmate session 56 | # # if: ${{ failure() }} 57 | # uses: mxschmitt/action-tmate@v3 58 | # with: 59 | # limit-access-to-actor: true -------------------------------------------------------------------------------- /.github/workflows/pre-commit.yml: -------------------------------------------------------------------------------- 1 | name: Pre-Commit 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: [ master ] 7 | 8 | jobs: 9 | pre-commit: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | - uses: actions/setup-python@v3 14 | - uses: actions/setup-go@v4 15 | with: 16 | go-version: 1.23 17 | - name: Install dependencies 18 | run: | 19 | 20 | go install honnef.co/go/tools/cmd/staticcheck@latest 21 | go install github.com/kisielk/errcheck@latest 22 | go install mvdan.cc/gofumpt@latest 23 | go install github.com/daixiang0/gci@latest 24 | - uses: pre-commit/action@v3.0.0 25 | with: 26 | extra_args: --all-files 27 | 28 | -------------------------------------------------------------------------------- /.github/workflows/server-releaser.yml: -------------------------------------------------------------------------------- 1 | name: Server Releaser 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | tags: 7 | - "*" 8 | 9 | permissions: 10 | contents: write 11 | 12 | jobs: 13 | releases-matrix: 14 | name: Release Go Binary 15 | runs-on: ubuntu-latest 16 | strategy: 17 | matrix: 18 | goos: [linux, darwin] 19 | goarch: [amd64, arm, arm64] 20 | exclude: 21 | - goos: darwin 22 | goarch: arm 23 | steps: 24 | - uses: actions/checkout@v2 25 | - name: Set up Go 26 | uses: actions/setup-go@v4 27 | with: 28 | go-version: 1.23 29 | - name: Build server binary 30 | run: | 31 | GOARCH=${{ matrix.goarch }} GOOS=${{ matrix.goos }} go build -o hishtory-server-${{ matrix.goos }}-${{ matrix.goarch }} backend/server/server.go 32 | - name: Release 33 | uses: softprops/action-gh-release@1e07f4398721186383de40550babbdf2b84acfc5 34 | if: ${{ startsWith(github.ref, 'refs/tags/') }} 35 | with: 36 | files: | 37 | hishtory-server-${{ matrix.goos }}-${{ matrix.goarch }} 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | web/landing/www/binaries/hishtory-linux 2 | hishtory 3 | backend/server/server 4 | postgres-data/ 5 | server 6 | !backend/server 7 | .DS_Store 8 | node_modules/ 9 | package.json 10 | package-lock.json 11 | .prettierrc 12 | *.cpuprof 13 | 14 | # VS Code settings 15 | .vscode/ 16 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/Bahjat/pre-commit-golang 3 | rev: bdba95f94147c2f5da7eda81e15cdd92c41758ba 4 | hooks: 5 | - id: go-vet 6 | - id: go-static-check # install https://staticcheck.io/docs/ 7 | - id: golangci-lint # requires github.com/golangci/golangci-lint 8 | - repo: local 9 | hooks: 10 | - id: go-errcheck 11 | name: go-errcheck 12 | entry: errcheck -exclude .errcheck_excludes.txt ./... 13 | language: system 14 | pass_filenames: false 15 | - id: make-fmt # requires 'go install mvdan.cc/gofumpt@latest' and 'go install github.com/daixiang0/gci@latest' 16 | name: make-fmt 17 | entry: make fmt 18 | language: system 19 | pass_filenames: false 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 David Dworken 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 335 2 | -------------------------------------------------------------------------------- /backend/server/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.23.0-alpine3.20 AS builder 2 | 3 | WORKDIR /app 4 | RUN apk add --update --no-cache --virtual .build-deps build-base 5 | COPY go.mod go.sum ./ 6 | RUN go mod download 7 | COPY . ./ 8 | ARG GOARCH 9 | RUN GOARCH=${GOARCH} go build -o /server -ldflags "-X main.ReleaseVersion=v0.`cat VERSION`" backend/server/server.go && \ 10 | apk del .build-deps 11 | 12 | FROM alpine:3.17 13 | COPY --from=builder /server /server 14 | CMD ["/server"] 15 | -------------------------------------------------------------------------------- /backend/server/docker-compose.yml: -------------------------------------------------------------------------------- 1 | # A docker-compose file to host a hiSHtory backend. To use: 2 | # 1. Update TODO_YOUR_POSTGRES_PASSWORD_HERE 3 | # 2. `docker compose -f backend/server/docker-compose.yml build` 4 | # 3. `docker compose -f backend/server/docker-compose.yml up` 5 | # 4. Point your hiSHtory client at the server by putting `export HISHTORY_SERVER=http://1.2.3.4` in your shellrc 6 | # 5. Run `hishtory init` to initialize hiSHtory with the local server 7 | # 6. [Optional, but recommended] Add a TLS proxy to enable https 8 | version: "3.8" 9 | networks: 10 | hishtory: 11 | driver: bridge 12 | services: 13 | postgres: 14 | image: postgres 15 | restart: unless-stopped 16 | networks: 17 | - hishtory 18 | environment: 19 | POSTGRES_PASSWORD: TODO_YOUR_POSTGRES_PASSWORD_HERE 20 | POSTGRES_DB: hishtory 21 | PGDATA: /var/lib/postgresql/data/pgdata 22 | volumes: 23 | - postgres-data:/var/lib/postgresql/data 24 | healthcheck: 25 | test: pg_isready -U postgres 26 | interval: 10s 27 | timeout: 3s 28 | hishtory: 29 | depends_on: 30 | postgres: 31 | condition: service_healthy 32 | networks: 33 | - hishtory 34 | build: 35 | context: ../../ 36 | dockerfile: ./backend/server/Dockerfile 37 | restart: unless-stopped 38 | deploy: 39 | restart_policy: 40 | condition: on-failure 41 | delay: 3s 42 | environment: 43 | HISHTORY_POSTGRES_DB: postgresql://postgres:TODO_YOUR_POSTGRES_PASSWORD_HERE@postgres:5432/hishtory?sslmode=disable 44 | HISHTORY_COMPOSE_TEST: $HISHTORY_COMPOSE_TEST 45 | ports: 46 | - 80:8080 47 | volumes: 48 | postgres-data: 49 | -------------------------------------------------------------------------------- /backend/server/internal/database/device.go: -------------------------------------------------------------------------------- 1 | package database 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | ) 8 | 9 | type Device struct { 10 | UserId string `json:"user_id"` 11 | DeviceId string `json:"device_id"` 12 | // The IP address that was used to register the device. Recorded so 13 | // that I can count how many people are using hishtory and roughly 14 | // from where. If you would like this deleted, please email me at 15 | // david@daviddworken.com and I can clear it from your device entries. 16 | RegistrationIp string `json:"registration_ip"` 17 | RegistrationDate time.Time `json:"registration_date"` 18 | // Test devices, that should be aggressively cleaned from the DB 19 | IsIntegrationTestDevice bool `json:"is_integration_test_device"` 20 | // Whether this device was uninstalled 21 | UninstallDate time.Time `json:"uninstall_date"` 22 | } 23 | 24 | func (db *DB) CountAllDevices(ctx context.Context) (int64, error) { 25 | var numDevices int64 = 0 26 | tx := db.WithContext(ctx).Model(&Device{}).Count(&numDevices) 27 | if tx.Error != nil { 28 | return 0, fmt.Errorf("tx.Error: %w", tx.Error) 29 | } 30 | 31 | return numDevices, nil 32 | } 33 | 34 | func (db *DB) CountDevicesForUser(ctx context.Context, userID string) (int64, error) { 35 | var existingDevicesCount int64 36 | tx := db.WithContext(ctx).Model(&Device{}).Where("user_id = ?", userID).Count(&existingDevicesCount) 37 | if tx.Error != nil { 38 | return 0, fmt.Errorf("tx.Error: %w", tx.Error) 39 | } 40 | 41 | return existingDevicesCount, nil 42 | } 43 | 44 | func (db *DB) CreateDevice(ctx context.Context, device *Device) error { 45 | tx := db.WithContext(ctx).Create(device) 46 | if tx.Error != nil { 47 | return fmt.Errorf("tx.Error: %w", tx.Error) 48 | } 49 | 50 | return nil 51 | } 52 | 53 | func (db *DB) DevicesForUser(ctx context.Context, userID string) ([]*Device, error) { 54 | var devices []*Device 55 | tx := db.WithContext(ctx).Where("user_id = ? AND (uninstall_date IS NULL OR uninstall_date < '1971-01-01')", userID).Find(&devices) 56 | if tx.Error != nil { 57 | return nil, fmt.Errorf("tx.Error: %w", tx.Error) 58 | } 59 | 60 | return devices, nil 61 | } 62 | -------------------------------------------------------------------------------- /backend/server/internal/release/release_test.go: -------------------------------------------------------------------------------- 1 | package release 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | 7 | "github.com/ddworken/hishtory/shared/testutils" 8 | 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func TestUpdateReleaseVersion(t *testing.T) { 13 | if !testutils.IsOnline() { 14 | t.Skip("skipping because we're currently offline") 15 | } 16 | 17 | // Check that ReleaseVersion hasn't been set yet 18 | if Version != "UNKNOWN" { 19 | t.Fatalf("initial ReleaseVersion isn't as expected: %#v", Version) 20 | } 21 | 22 | // Update it 23 | err := UpdateReleaseVersion() 24 | if err != nil { 25 | t.Fatalf("updateReleaseVersion failed: %v", err) 26 | } 27 | 28 | // If ReleaseVersion is still unknown, skip because we're getting rate limited 29 | if Version == "UNKNOWN" { 30 | t.Skip() 31 | } 32 | // Otherwise, check that the new value looks reasonable 33 | if !strings.HasPrefix(Version, "v0.") { 34 | t.Fatalf("ReleaseVersion wasn't updated to contain a version: %#v", Version) 35 | } 36 | } 37 | 38 | func TestDecrement(t *testing.T) { 39 | pv, err := decrementVersion("v0.100") 40 | require.NoError(t, err) 41 | require.Equal(t, "v0.99", pv) 42 | } 43 | -------------------------------------------------------------------------------- /backend/server/internal/server/util.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "net/http" 7 | pprofhttp "net/http/pprof" 8 | "os" 9 | "runtime" 10 | "strconv" 11 | 12 | httptrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/net/http" 13 | "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" 14 | "gopkg.in/DataDog/dd-trace-go.v1/profiler" 15 | ) 16 | 17 | func getMaximumNumberOfAllowedUsers() int { 18 | maxNumUsersStr := os.Getenv("HISHTORY_MAX_NUM_USERS") 19 | if maxNumUsersStr == "" { 20 | return math.MaxInt 21 | } 22 | maxNumUsers, err := strconv.Atoi(maxNumUsersStr) 23 | if err != nil { 24 | return math.MaxInt 25 | } 26 | return maxNumUsers 27 | } 28 | 29 | func configureObservability(mux *httptrace.ServeMux, releaseVersion string) func() { 30 | // Profiler 31 | err := profiler.Start( 32 | profiler.WithService("hishtory-api"), 33 | profiler.WithVersion(releaseVersion), 34 | profiler.WithAPIKey(os.Getenv("DD_API_KEY")), 35 | profiler.WithUDS("/var/run/datadog/apm.socket"), 36 | profiler.WithProfileTypes( 37 | profiler.CPUProfile, 38 | profiler.HeapProfile, 39 | ), 40 | ) 41 | if err != nil { 42 | fmt.Printf("Failed to start DataDog profiler: %v\n", err) 43 | } 44 | // Tracer 45 | tracer.Start( 46 | tracer.WithRuntimeMetrics(), 47 | tracer.WithService("hishtory-api"), 48 | tracer.WithUDS("/var/run/datadog/apm.socket"), 49 | ) 50 | 51 | // Pprof 52 | mux.HandleFunc("/debug/pprof/", pprofhttp.Index) 53 | mux.HandleFunc("/debug/pprof/cmdline", pprofhttp.Cmdline) 54 | mux.HandleFunc("/debug/pprof/profile", pprofhttp.Profile) 55 | mux.HandleFunc("/debug/pprof/symbol", pprofhttp.Symbol) 56 | mux.HandleFunc("/debug/pprof/trace", pprofhttp.Trace) 57 | 58 | // Func to stop all of the above 59 | return func() { 60 | profiler.Stop() 61 | tracer.Stop() 62 | } 63 | } 64 | 65 | func getHishtoryVersion(r *http.Request) string { 66 | return r.Header.Get("X-Hishtory-Version") 67 | } 68 | 69 | func getRemoteAddr(r *http.Request) string { 70 | addr, ok := r.Header["X-Real-Ip"] 71 | if !ok || len(addr) == 0 { 72 | return r.RemoteAddr 73 | } 74 | return addr[0] 75 | } 76 | 77 | func getRequiredQueryParam(r *http.Request, queryParam string) string { 78 | val := r.URL.Query().Get(queryParam) 79 | if val == "" { 80 | panic(fmt.Sprintf("request to %s is missing required query param=%#v", r.URL, queryParam)) 81 | } 82 | return val 83 | } 84 | 85 | func getOptionalQueryParam(r *http.Request, queryParam string, isRequiredInTestEnvironment bool) string { 86 | val := r.URL.Query().Get(queryParam) 87 | if val == "" && isRequiredInTestEnvironment { 88 | panic(fmt.Sprintf("request to %s is missing optional query param=%#v that is required in test environments", r.URL, queryParam)) 89 | } 90 | return val 91 | } 92 | 93 | func checkGormError(err error) { 94 | if err == nil { 95 | return 96 | } 97 | 98 | _, filename, line, _ := runtime.Caller(1) 99 | panic(fmt.Sprintf("DB error at %s:%d: %v", filename, line, err)) 100 | } 101 | -------------------------------------------------------------------------------- /backend/web/caddy/Caddyfile: -------------------------------------------------------------------------------- 1 | hishtory.dev:80, localhost:80 { 2 | root /srv/landing 3 | gzip 4 | ext .html 5 | log stdout 6 | tls off 7 | } 8 | -------------------------------------------------------------------------------- /backend/web/caddy/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM abiosoft/caddy 2 | 3 | LABEL "com.datadoghq.ad.logs"='[{"source": "caddy", "service": "web"}]' 4 | 5 | COPY backend/web/caddy/Caddyfile /etc/ 6 | COPY backend/web/landing/www/ /srv/landing 7 | -------------------------------------------------------------------------------- /backend/web/landing/www/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | bower_components -------------------------------------------------------------------------------- /backend/web/landing/www/img/aidemo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddworken/hishtory/0e89432849b257aba3c0383f7264d5dbfe7a68a2/backend/web/landing/www/img/aidemo.png -------------------------------------------------------------------------------- /backend/web/landing/www/img/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddworken/hishtory/0e89432849b257aba3c0383f7264d5dbfe7a68a2/backend/web/landing/www/img/demo.gif -------------------------------------------------------------------------------- /backend/web/landing/www/img/webui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddworken/hishtory/0e89432849b257aba3c0383f7264d5dbfe7a68a2/backend/web/landing/www/img/webui.png -------------------------------------------------------------------------------- /backend/web/landing/www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

See here.

8 | 9 | 10 | -------------------------------------------------------------------------------- /backend/web/landing/www/install.py: -------------------------------------------------------------------------------- 1 | """ 2 | A small install script to download the correct hishtory binary for the current OS/architecture. 3 | The hishtory binary is in charge of installing itself, this just downloads the correct binary and 4 | executes it. 5 | """ 6 | 7 | import json 8 | import urllib.request 9 | import platform 10 | import sys 11 | import os 12 | 13 | def get_executable_tmpdir(): 14 | specified_dir = os.environ.get('TMPDIR', '') 15 | if specified_dir: 16 | return specified_dir 17 | try: 18 | if hasattr(os, 'ST_NOEXEC'): 19 | if os.statvfs("/tmp").f_flag & os.ST_NOEXEC: 20 | # /tmp/ is mounted as NOEXEC, so fall back to the current working directory 21 | return os.getcwd() 22 | except: 23 | pass 24 | return "/tmp/" 25 | 26 | with urllib.request.urlopen('https://api.hishtory.dev/api/v1/download') as response: 27 | resp_body = response.read() 28 | download_options = json.loads(resp_body) 29 | 30 | if platform.system() == 'Linux' and platform.machine() == "x86_64": 31 | download_url = download_options['linux_amd_64_url'] 32 | elif platform.system() == 'Linux' and platform.machine() == "aarch64": 33 | download_url = download_options['linux_arm_64_url'] 34 | elif platform.system() == 'Linux' and platform.machine() == "armv7l": 35 | download_url = download_options['linux_arm_7_url'] 36 | elif platform.system() == 'Darwin' and platform.machine() == 'arm64': 37 | download_url = download_options['darwin_arm_64_url'] 38 | elif platform.system() == 'Darwin' and platform.machine() == 'x86_64': 39 | download_url = download_options['darwin_amd_64_url'] 40 | else: 41 | print(f"No hishtory binary for system={platform.system()}, machine={platform.machine()}!\nIf you believe this is a mistake, please open an issue here: https://github.com/ddworken/hishtory/issues") 42 | sys.exit(1) 43 | 44 | with urllib.request.urlopen(download_url) as response: 45 | hishtory_binary = response.read() 46 | 47 | tmpFilePath = os.path.join(get_executable_tmpdir(), 'hishtory-client') 48 | if os.path.exists(tmpFilePath): 49 | os.remove(tmpFilePath) 50 | with open(tmpFilePath, 'wb') as f: 51 | f.write(hishtory_binary) 52 | os.system('chmod +x ' + tmpFilePath) 53 | cmd = tmpFilePath + ' install' 54 | if os.environ.get('HISHTORY_OFFLINE'): 55 | cmd += " --offline" 56 | additional_flags = [flag for flag in sys.argv[1:] if flag.startswith("-") and flag != "-" and flag != "--"] 57 | if additional_flags: 58 | cmd += " " + " ".join(additional_flags) 59 | exitCode = os.system(cmd) 60 | os.remove(tmpFilePath) 61 | if exitCode != 0: 62 | raise Exception("failed to install downloaded hishtory client via `" + tmpFilePath +" install` (is that directory mounted noexec? Consider setting an alternate directory via the TMPDIR environment variable)!") 63 | print('Succesfully installed hishtory! Open a new terminal, try running a command, and then running `hishtory query`.') 64 | -------------------------------------------------------------------------------- /client/cmd/configAdd.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | 8 | "github.com/ddworken/hishtory/client/hctx" 9 | "github.com/ddworken/hishtory/client/lib" 10 | 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | var configAddCmd = &cobra.Command{ 15 | Use: "config-add", 16 | Short: "Add a config option", 17 | GroupID: GROUP_ID_CONFIG, 18 | Run: func(cmd *cobra.Command, args []string) { 19 | lib.CheckFatalError(cmd.Help()) 20 | os.Exit(1) 21 | }, 22 | } 23 | 24 | var addCustomColumnsCmd = &cobra.Command{ 25 | Use: "custom-columns", 26 | Aliases: []string{"custom-column"}, 27 | Short: "Add a custom column", 28 | Args: cobra.ExactArgs(2), 29 | Run: func(cmd *cobra.Command, args []string) { 30 | columnName := args[0] 31 | command := args[1] 32 | ctx := hctx.MakeContext() 33 | config := hctx.GetConf(ctx) 34 | if config.CustomColumns == nil { 35 | config.CustomColumns = make([]hctx.CustomColumnDefinition, 0) 36 | } 37 | for _, existingColumn := range config.CustomColumns { 38 | if strings.EqualFold(existingColumn.ColumnName, columnName) { 39 | lib.CheckFatalError(fmt.Errorf("cannot create a column named %#v since there is already one named %#v", existingColumn.ColumnName, columnName)) 40 | } 41 | } 42 | config.CustomColumns = append(config.CustomColumns, hctx.CustomColumnDefinition{ColumnName: columnName, ColumnCommand: command}) 43 | lib.CheckFatalError(hctx.SetConfig(config)) 44 | }, 45 | } 46 | 47 | var addDisplayedColumnsCmd = &cobra.Command{ 48 | Use: "displayed-columns", 49 | Aliases: []string{"displayed-column"}, 50 | Short: "Add a column to be displayed", 51 | Args: cobra.ExactArgs(1), 52 | Run: func(cmd *cobra.Command, args []string) { 53 | ctx := hctx.MakeContext() 54 | config := hctx.GetConf(ctx) 55 | config.DisplayedColumns = append(config.DisplayedColumns, args...) 56 | lib.CheckFatalError(hctx.SetConfig(config)) 57 | }, 58 | } 59 | 60 | var addDefaultSearchColumnsCmd = &cobra.Command{ 61 | Use: "default-search-columns", 62 | Aliases: []string{"default-search-column"}, 63 | Short: "Add a column that is used for \"default\" search queries that don't use any search atoms", 64 | Long: "By default hishtory queries are checked against `command`, `current_working_directory`, and `hostname`. This option can be used to add additional columns to the list of columns checked in default queries. E.g. to add a custom column named `git_remote` to the list of default search columns, you would run `hishtory config-add default-search-columns git_remote`", 65 | Args: cobra.ExactArgs(1), 66 | Run: func(cmd *cobra.Command, args []string) { 67 | ctx := hctx.MakeContext() 68 | config := hctx.GetConf(ctx) 69 | lib.CheckFatalError(validateDefaultSearchColumns(ctx, args)) 70 | config.DefaultSearchColumns = append(config.DefaultSearchColumns, args...) 71 | lib.CheckFatalError(hctx.SetConfig(config)) 72 | }, 73 | } 74 | 75 | func init() { 76 | rootCmd.AddCommand(configAddCmd) 77 | configAddCmd.AddCommand(addCustomColumnsCmd) 78 | configAddCmd.AddCommand(addDisplayedColumnsCmd) 79 | configAddCmd.AddCommand(addDefaultSearchColumnsCmd) 80 | } 81 | -------------------------------------------------------------------------------- /client/cmd/enableDisable.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/ddworken/hishtory/client/hctx" 7 | "github.com/ddworken/hishtory/client/lib" 8 | 9 | "github.com/spf13/cobra" 10 | ) 11 | 12 | var enableCmd = &cobra.Command{ 13 | Use: "enable", 14 | Short: "Enable hiSHtory recording", 15 | GroupID: GROUP_ID_CONFIG, 16 | Run: func(cmd *cobra.Command, args []string) { 17 | ctx := hctx.MakeContext() 18 | lib.CheckFatalError(Enable(ctx)) 19 | }, 20 | } 21 | 22 | var disableCmd = &cobra.Command{ 23 | Use: "disable", 24 | Short: "Disable hiSHtory recording", 25 | GroupID: GROUP_ID_CONFIG, 26 | Run: func(cmd *cobra.Command, args []string) { 27 | ctx := hctx.MakeContext() 28 | lib.CheckFatalError(Disable(ctx)) 29 | }, 30 | } 31 | 32 | func Enable(ctx context.Context) error { 33 | config := hctx.GetConf(ctx) 34 | config.IsEnabled = true 35 | return hctx.SetConfig(config) 36 | } 37 | 38 | func Disable(ctx context.Context) error { 39 | config := hctx.GetConf(ctx) 40 | config.IsEnabled = false 41 | return hctx.SetConfig(config) 42 | } 43 | 44 | func init() { 45 | rootCmd.AddCommand(enableCmd) 46 | rootCmd.AddCommand(disableCmd) 47 | } 48 | -------------------------------------------------------------------------------- /client/cmd/export.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "io" 8 | "os" 9 | 10 | "github.com/ddworken/hishtory/client/data" 11 | "github.com/ddworken/hishtory/client/hctx" 12 | "github.com/ddworken/hishtory/client/lib" 13 | 14 | "github.com/spf13/cobra" 15 | ) 16 | 17 | var exportJsonCmd = &cobra.Command{ 18 | Use: "export-json", 19 | Short: "Export history entries formatted in JSON lines format (as accepted by hishtory import-json, and easily parsable by other tools)", 20 | GroupID: GROUP_ID_MANAGEMENT, 21 | Run: func(cmd *cobra.Command, args []string) { 22 | ctx := hctx.MakeContext() 23 | err := exportToJson(ctx, os.Stdout) 24 | lib.CheckFatalError(err) 25 | }, 26 | } 27 | 28 | func structToMap(entry data.HistoryEntry) (map[string]interface{}, error) { 29 | inrec, err := json.Marshal(entry) 30 | if err != nil { 31 | return nil, err 32 | } 33 | var m map[string]interface{} 34 | err = json.Unmarshal(inrec, &m) 35 | return m, err 36 | } 37 | 38 | func exportToJson(ctx context.Context, w io.Writer) error { 39 | db := hctx.GetDb(ctx) 40 | chunkSize := 1000 41 | offset := 0 42 | for { 43 | entries, err := lib.SearchWithOffset(ctx, db, "", chunkSize, offset) 44 | if err != nil { 45 | return fmt.Errorf("failed to search for history entries with offset=%d: %w", offset, err) 46 | } 47 | if len(entries) == 0 { 48 | break 49 | } 50 | for _, entry := range entries { 51 | if entry.Command == "" { 52 | // Skip empty commands, see https://github.com/ddworken/hishtory/issues/279 53 | continue 54 | } 55 | m, err := structToMap(*entry) 56 | if err != nil { 57 | return err 58 | } 59 | delete(m, "device_id") 60 | delete(m, "entry_id") 61 | j, err := json.Marshal(m) 62 | if err != nil { 63 | return err 64 | } 65 | _, err = w.Write(j) 66 | if err != nil { 67 | return err 68 | } 69 | _, err = w.Write([]byte("\n")) 70 | if err != nil { 71 | return err 72 | } 73 | } 74 | offset += chunkSize 75 | } 76 | return nil 77 | } 78 | 79 | func init() { 80 | rootCmd.AddCommand(exportJsonCmd) 81 | } 82 | -------------------------------------------------------------------------------- /client/cmd/install_test.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "os" 5 | "path" 6 | "testing" 7 | 8 | "github.com/ddworken/hishtory/client/data" 9 | "github.com/ddworken/hishtory/client/hctx" 10 | "github.com/ddworken/hishtory/shared/testutils" 11 | 12 | "github.com/stretchr/testify/require" 13 | ) 14 | 15 | func TestSetup(t *testing.T) { 16 | defer testutils.BackupAndRestore(t)() 17 | defer testutils.RunTestServer()() 18 | 19 | homedir, err := os.UserHomeDir() 20 | require.NoError(t, err) 21 | if _, err := os.Stat(path.Join(homedir, data.GetHishtoryPath(), data.CONFIG_PATH)); err == nil { 22 | t.Fatalf("hishtory secret file already exists!") 23 | } 24 | require.NoError(t, setup("", false)) 25 | if _, err := os.Stat(path.Join(homedir, data.GetHishtoryPath(), data.CONFIG_PATH)); err != nil { 26 | t.Fatalf("hishtory secret file does not exist after Setup()!") 27 | } 28 | data, err := os.ReadFile(path.Join(homedir, data.GetHishtoryPath(), data.CONFIG_PATH)) 29 | require.NoError(t, err) 30 | if len(data) < 10 { 31 | t.Fatalf("hishtory secret has unexpected length: %d", len(data)) 32 | } 33 | config := hctx.GetConf(hctx.MakeContext()) 34 | if config.IsOffline != false { 35 | t.Fatalf("hishtory config should have been offline") 36 | } 37 | } 38 | 39 | func TestSetupOffline(t *testing.T) { 40 | defer testutils.BackupAndRestore(t)() 41 | defer testutils.RunTestServer()() 42 | 43 | homedir, err := os.UserHomeDir() 44 | require.NoError(t, err) 45 | if _, err := os.Stat(path.Join(homedir, data.GetHishtoryPath(), data.CONFIG_PATH)); err == nil { 46 | t.Fatalf("hishtory secret file already exists!") 47 | } 48 | require.NoError(t, setup("", true)) 49 | if _, err := os.Stat(path.Join(homedir, data.GetHishtoryPath(), data.CONFIG_PATH)); err != nil { 50 | t.Fatalf("hishtory secret file does not exist after Setup()!") 51 | } 52 | data, err := os.ReadFile(path.Join(homedir, data.GetHishtoryPath(), data.CONFIG_PATH)) 53 | require.NoError(t, err) 54 | if len(data) < 10 { 55 | t.Fatalf("hishtory secret has unexpected length: %d", len(data)) 56 | } 57 | config := hctx.GetConf(hctx.MakeContext()) 58 | if config.IsOffline != true { 59 | t.Fatalf("hishtory config should have been offline, actual=%#v", string(data)) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /client/cmd/reupload.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/ddworken/hishtory/client/hctx" 5 | "github.com/ddworken/hishtory/client/lib" 6 | 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | var reuploadCmd = &cobra.Command{ 11 | Use: "reupload", 12 | Hidden: true, 13 | Short: "[Debug Only] Reupload your entire hiSHtory to all other devices", 14 | Run: func(cmd *cobra.Command, args []string) { 15 | lib.CheckFatalError(lib.Reupload(hctx.MakeContext())) 16 | }, 17 | } 18 | 19 | func init() { 20 | rootCmd.AddCommand(reuploadCmd) 21 | } 22 | -------------------------------------------------------------------------------- /client/cmd/root.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2022 NAME HERE 3 | */ 4 | package cmd 5 | 6 | import ( 7 | "os" 8 | 9 | "github.com/ddworken/hishtory/client/lib" 10 | 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | // rootCmd represents the base command when called without any subcommands 15 | var rootCmd = &cobra.Command{ 16 | Use: "hishtory", 17 | Short: "hiSHtory: Better shell history", 18 | } 19 | 20 | // Execute adds all child commands to the root command and sets flags appropriately. 21 | // This is called by main.main(). It only needs to happen once to the rootCmd. 22 | func Execute() { 23 | err := rootCmd.Execute() 24 | if err != nil { 25 | os.Exit(1) 26 | } 27 | } 28 | 29 | func init() { 30 | rootCmd.AddGroup(&cobra.Group{ID: GROUP_ID_QUERYING, Title: "History Searching"}) 31 | rootCmd.AddGroup(&cobra.Group{ID: GROUP_ID_MANAGEMENT, Title: "History Management"}) 32 | rootCmd.AddGroup(&cobra.Group{ID: GROUP_ID_CONFIG, Title: "Configuration"}) 33 | rootCmd.AddGroup(&cobra.Group{ID: GROUP_ID_INSTALL, Title: "Installation"}) 34 | rootCmd.Version = "v0." + lib.Version 35 | } 36 | -------------------------------------------------------------------------------- /client/cmd/status.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/ddworken/hishtory/client/data" 8 | "github.com/ddworken/hishtory/client/hctx" 9 | "github.com/ddworken/hishtory/client/lib" 10 | 11 | "github.com/spf13/cobra" 12 | "gopkg.in/yaml.v3" 13 | ) 14 | 15 | var ( 16 | verbose *bool 17 | configFlag *bool 18 | ) 19 | 20 | var statusCmd = &cobra.Command{ 21 | Use: "status", 22 | Short: "View status info including the secret key which is needed to sync shell history from another machine", 23 | Run: func(cmd *cobra.Command, args []string) { 24 | ctx := hctx.MakeContext() 25 | config := hctx.GetConf(ctx) 26 | fmt.Printf("hiSHtory: v0.%s\nEnabled: %v\n", lib.Version, config.IsEnabled) 27 | fmt.Printf("Secret Key: %s\n", config.UserSecret) 28 | if *verbose { 29 | fmt.Printf("User ID: %s\n", data.UserId(config.UserSecret)) 30 | fmt.Printf("Device ID: %s\n", config.DeviceId) 31 | printOnlineStatus(config) 32 | } 33 | fmt.Printf("Commit Hash: %s\n", lib.GitCommit) 34 | if *configFlag { 35 | y, err := yaml.Marshal(config) 36 | if err != nil { 37 | lib.CheckFatalError(fmt.Errorf("failed to marshal config to yaml: %w", err)) 38 | } 39 | indented := "\t" + strings.ReplaceAll(string(y), "\n", "\n\t") 40 | fmt.Printf("Full Config:\n%s\n", indented) 41 | } 42 | }, 43 | } 44 | 45 | func printOnlineStatus(config *hctx.ClientConfig) { 46 | if config.IsOffline { 47 | fmt.Println("Sync Mode: Disabled") 48 | } else { 49 | fmt.Println("Sync Mode: Enabled") 50 | if lib.GetServerHostname() != lib.DefaultServerHostname { 51 | fmt.Println("Sync Server: " + lib.GetServerHostname()) 52 | } 53 | if config.HaveMissedUploads || len(config.PendingDeletionRequests) > 0 { 54 | fmt.Println("Sync Status: Unsynced (device is offline?)") 55 | fmt.Printf(" HaveMissedUploads=%v MissedUploadTimestamp=%v len(PendingDeletionRequests)=%v\n", config.HaveMissedUploads, config.MissedUploadTimestamp, len(config.PendingDeletionRequests)) 56 | } else { 57 | fmt.Println("Sync Status: Synced") 58 | } 59 | } 60 | } 61 | 62 | func init() { 63 | rootCmd.AddCommand(statusCmd) 64 | verbose = statusCmd.Flags().BoolP("verbose", "v", false, "Display verbose hiSHtory information") 65 | configFlag = statusCmd.Flags().Bool("full-config", false, "Display hiSHtory's full config") 66 | } 67 | -------------------------------------------------------------------------------- /client/cmd/syncing.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/ddworken/hishtory/client/data" 8 | "github.com/ddworken/hishtory/client/hctx" 9 | "github.com/ddworken/hishtory/client/lib" 10 | 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | var syncingCmd = &cobra.Command{ 15 | Use: "syncing", 16 | Short: "Configure syncing to enable or disable syncing with the hishtory backend", 17 | Long: "Run `hishtory syncing disable` to disable syncing and `hishtory syncing enable` to enable syncing.", 18 | ValidArgs: []string{"disable", "enable"}, 19 | Args: cobra.MatchAll(cobra.OnlyValidArgs, cobra.ExactArgs(1)), 20 | Run: func(cmd *cobra.Command, args []string) { 21 | syncingStatus := false 22 | if args[0] == "disable" { 23 | syncingStatus = false 24 | } else if args[0] == "enable" { 25 | syncingStatus = true 26 | } else { 27 | lib.CheckFatalError(fmt.Errorf("unexpected syncing argument %q", args[0])) 28 | } 29 | 30 | ctx := hctx.MakeContext() 31 | conf := hctx.GetConf(ctx) 32 | if syncingStatus { 33 | if conf.IsOffline { 34 | lib.CheckFatalError(switchToOnline(ctx)) 35 | fmt.Println("Enabled syncing successfully") 36 | } else { 37 | lib.CheckFatalError(fmt.Errorf("device is already online")) 38 | } 39 | } else { 40 | if conf.IsOffline { 41 | lib.CheckFatalError(fmt.Errorf("device is already offline")) 42 | } else { 43 | lib.CheckFatalError(switchToOffline(ctx)) 44 | fmt.Println("Disabled syncing successfully") 45 | } 46 | } 47 | }, 48 | } 49 | 50 | func switchToOnline(ctx context.Context) error { 51 | config := hctx.GetConf(ctx) 52 | config.IsOffline = false 53 | err := hctx.SetConfig(config) 54 | if err != nil { 55 | return fmt.Errorf("failed to switch device to online due to error while setting config: %w", err) 56 | } 57 | err = registerAndBootstrapDevice(ctx, config, hctx.GetDb(ctx), config.UserSecret) 58 | if err != nil { 59 | return fmt.Errorf("failed to register device with backend: %w", err) 60 | } 61 | err = lib.Reupload(ctx) 62 | if err != nil { 63 | return fmt.Errorf("failed to switch device to online due to error while uploading history entries: %w", err) 64 | } 65 | return nil 66 | } 67 | 68 | func switchToOffline(ctx context.Context) error { 69 | config := hctx.GetConf(ctx) 70 | config.IsOffline = true 71 | err := hctx.SetConfig(config) 72 | if err != nil { 73 | return fmt.Errorf("failed to switch device to offline due to error while setting config: %w", err) 74 | } 75 | _, err = lib.ApiPost(ctx, "/api/v1/uninstall?user_id="+data.UserId(hctx.GetConf(ctx).UserSecret)+"&device_id="+hctx.GetConf(ctx).DeviceId, "application/json", []byte{}) 76 | if err != nil { 77 | return fmt.Errorf("failed to switch device to offline due to error while deleting sync state: %w", err) 78 | } 79 | return nil 80 | } 81 | 82 | func init() { 83 | rootCmd.AddCommand(syncingCmd) 84 | } 85 | -------------------------------------------------------------------------------- /client/cmd/webui.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | 8 | "github.com/ddworken/hishtory/client/hctx" 9 | "github.com/ddworken/hishtory/client/lib" 10 | "github.com/ddworken/hishtory/client/webui" 11 | 12 | "github.com/spf13/cobra" 13 | ) 14 | 15 | var ( 16 | disableAuth *bool 17 | forceCreds *string 18 | port *int 19 | ) 20 | 21 | var webUiCmd = &cobra.Command{ 22 | Use: "start-web-ui", 23 | Short: "Serve a basic web UI for interacting with your shell history", 24 | Run: func(cmd *cobra.Command, args []string) { 25 | overridenUsername := "" 26 | overridenPassword := "" 27 | if *forceCreds != "" { 28 | if strings.Contains(*forceCreds, ":") { 29 | splitCreds := strings.SplitN(*forceCreds, ":", 2) 30 | overridenUsername = splitCreds[0] 31 | overridenPassword = splitCreds[1] 32 | } else { 33 | lib.CheckFatalError(fmt.Errorf("--force-creds=%#v doesn't contain a colon to delimit username and password", *forceCreds)) 34 | } 35 | } 36 | if *disableAuth && *forceCreds != "" { 37 | lib.CheckFatalError(fmt.Errorf("cannot specify both --disable-auth and --force-creds")) 38 | } 39 | lib.CheckFatalError(webui.StartWebUiServer(hctx.MakeContext(), *port, *disableAuth, overridenUsername, overridenPassword)) 40 | os.Exit(1) 41 | }, 42 | } 43 | 44 | func init() { 45 | rootCmd.AddCommand(webUiCmd) 46 | disableAuth = webUiCmd.Flags().Bool("disable-auth", false, "Disable authentication for the Web UI (Warning: This means your entire shell history will be accessible from the local web server)") 47 | forceCreds = webUiCmd.Flags().String("force-creds", "", "Specify the credentials to use for basic auth in the form `user:password`") 48 | port = webUiCmd.Flags().Int("port", 8000, "The port for the web server to listen on") 49 | } 50 | -------------------------------------------------------------------------------- /client/data/data_test.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestEncryptDecrypt(t *testing.T) { 8 | k1 := EncryptionKey("key") 9 | k2 := EncryptionKey("key") 10 | if string(k1) != string(k2) { 11 | t.Fatalf("Expected EncryptionKey to be deterministic!") 12 | } 13 | 14 | ciphertext, nonce, err := Encrypt("key", []byte("hello world!"), []byte("extra")) 15 | checkError(t, err) 16 | plaintext, err := Decrypt("key", ciphertext, []byte("extra"), nonce) 17 | checkError(t, err) 18 | if string(plaintext) != "hello world!" { 19 | t.Fatalf("Expected decrypt(encrypt(x)) to work, but it didn't!") 20 | } 21 | } 22 | 23 | func checkError(t *testing.T, err error) { 24 | if err != nil { 25 | t.Fatal(err) 26 | } 27 | } 28 | 29 | func TestCustomColumnSerialization(t *testing.T) { 30 | cc1 := CustomColumn{ 31 | Name: "name1", 32 | Val: "val1", 33 | } 34 | cc2 := CustomColumn{ 35 | Name: "name2", 36 | Val: "val2", 37 | } 38 | var ccs CustomColumns = make(CustomColumns, 0) 39 | 40 | // Empty array 41 | v, err := ccs.Value() 42 | if err != nil { 43 | t.Fatalf("unexpected err: %v", err) 44 | } 45 | val := string(v.([]uint8)) 46 | if val != "[]" { 47 | t.Fatalf("unexpected val for empty CustomColumns: %#v", val) 48 | } 49 | 50 | // Non-empty array 51 | ccs = append(ccs, cc1, cc2) 52 | v, err = ccs.Value() 53 | if err != nil { 54 | t.Fatalf("unexpected err: %v", err) 55 | } 56 | val = string(v.([]uint8)) 57 | if val != "[{\"name\":\"name1\",\"value\":\"val1\"},{\"name\":\"name2\",\"value\":\"val2\"}]" { 58 | t.Fatalf("unexpected val for empty CustomColumns: %#v", val) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /client/lib/config.fish: -------------------------------------------------------------------------------- 1 | # For detecting color rendering support for this terminal, see #134 2 | hishtory getColorSupport 3 | export _hishtory_tui_color=$status 4 | 5 | function _hishtory_post_exec --on-event fish_preexec 6 | # Runs after , but before the command is executed 7 | set --global _hishtory_command $argv 8 | set --global _hishtory_start_time (hishtory getTimestamp) 9 | hishtory presaveHistoryEntry fish "$_hishtory_command" $_hishtory_start_time 2>&1 >/dev/null &; disown # Background Run 10 | # hishtory presaveHistoryEntry fish "$_hishtory_command" $_hishtory_start_time 2>&1 >/dev/null # Foreground Run 11 | end 12 | 13 | set --global _hishtory_first_prompt 1 14 | 15 | function __hishtory_on_prompt --on-event fish_prompt 16 | # Runs after the command is executed in order to render the prompt 17 | # $? contains the exit code 18 | set _hishtory_exit_code $status 19 | if [ -n "$_hishtory_first_prompt" ] 20 | set --global -e _hishtory_first_prompt 21 | else if [ -n "$_hishtory_command" ] 22 | hishtory saveHistoryEntry fish $_hishtory_exit_code "$_hishtory_command" $_hishtory_start_time 2>&1 >/dev/null &; disown # Background Run 23 | # hishtory saveHistoryEntry fish $_hishtory_exit_code "$_hishtory_command" $_hishtory_start_time 2>&1 >/dev/null # Foreground Run 24 | hishtory updateLocalDbFromRemote 2>&1 >/dev/null &; disown 25 | 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 26 | end 27 | end 28 | 29 | function __hishtory_on_control_r 30 | set -l tmp (mktemp -t fish.XXXXXX) 31 | set -x init_query (commandline -b) 32 | HISHTORY_TERM_INTEGRATION=1 HISHTORY_SHELL_NAME=fish hishtory tquery $init_query > $tmp 33 | set -l res $status 34 | commandline -f repaint 35 | if [ -s $tmp ] 36 | commandline -r -- (cat $tmp) 37 | end 38 | rm -f $tmp 39 | end 40 | 41 | [ (hishtory config-get enable-control-r) = true ] && bind \cr __hishtory_on_control_r 42 | 43 | hishtory completion fish | source -------------------------------------------------------------------------------- /client/lib/config.zsh: -------------------------------------------------------------------------------- 1 | autoload -U add-zsh-hook 2 | add-zsh-hook zshaddhistory _hishtory_add 3 | add-zsh-hook precmd _hishtory_precmd 4 | 5 | _hishtory_first_prompt=1 6 | 7 | # For detecting color rendering support for this terminal, see #134 8 | hishtory getColorSupport 9 | export _hishtory_tui_color=$? 10 | 11 | function _hishtory_add() { 12 | # Runs after , but before the command is executed 13 | # $1 contains the command that was run 14 | _hishtory_command=$1 15 | _hishtory_start_time=`hishtory getTimestamp` 16 | if ! [ -z "$_hishtory_command " ]; then 17 | (hishtory presaveHistoryEntry zsh "$_hishtory_command" $_hishtory_start_time &) 2>&1 >/dev/null # Background Run 18 | # hishtory presaveHistoryEntry zsh "$_hishtory_command" $_hishtory_start_time 2>&1 >/dev/null # Foreground Run 19 | fi 20 | } 21 | 22 | function _hishtory_precmd() { 23 | # Runs after the command is executed in order to render the prompt 24 | # $? contains the exit code 25 | _hishtory_exit_code=$? 26 | if [ -n "${_hishtory_first_prompt:-}" ]; then 27 | unset _hishtory_first_prompt 28 | return 29 | fi 30 | (hishtory saveHistoryEntry zsh $_hishtory_exit_code "$_hishtory_command" $_hishtory_start_time &) 2>&1 >/dev/null # Background Run 31 | # hishtory saveHistoryEntry zsh $_hishtory_exit_code "$_hishtory_command" $_hishtory_start_time 2>&1 >/dev/null # Foreground Run 32 | (hishtory updateLocalDbFromRemote &) 2>&1 >/dev/null 33 | } 34 | 35 | _hishtory_widget() { 36 | BUFFER=$(HISHTORY_TERM_INTEGRATION=1 HISHTORY_SHELL_NAME=zsh hishtory tquery $BUFFER) 37 | CURSOR=${#BUFFER} 38 | zle reset-prompt 39 | } 40 | 41 | _hishtory_bind_control_r() { 42 | zle -N _hishtory_widget 43 | bindkey '^R' _hishtory_widget 44 | } 45 | 46 | [ "$(hishtory config-get enable-control-r)" = true ] && _hishtory_bind_control_r 47 | 48 | # If running in a test environment, force loading of compinit so that shell completions work. 49 | # Otherwise, we respect the user's choice and only run compdef if the user has loaded compinit. 50 | if [ -n "${HISHTORY_TEST:-}" ]; then 51 | autoload -Uz compinit 52 | compinit 53 | fi 54 | 55 | source <(hishtory completion zsh); which compdef >/dev/null 2>&1 && compdef _hishtory hishtory || true -------------------------------------------------------------------------------- /client/lib/net.go: -------------------------------------------------------------------------------- 1 | //go:build !offline 2 | // +build !offline 3 | 4 | package lib 5 | 6 | import ( 7 | "net/http" 8 | ) 9 | 10 | func GetHttpClient() *http.Client { 11 | return http.DefaultClient 12 | } 13 | 14 | func IsOfflineBinary() bool { 15 | return false 16 | } 17 | -------------------------------------------------------------------------------- /client/lib/net_disabled.go: -------------------------------------------------------------------------------- 1 | //go:build offline 2 | // +build offline 3 | 4 | package lib 5 | 6 | import "net/http" 7 | 8 | func GetHttpClient() *http.Client { 9 | panic("Cannot GetHttpClient() from a hishtory client compiled with the offline tag!") 10 | } 11 | 12 | func IsOfflineBinary() bool { 13 | return true 14 | } 15 | -------------------------------------------------------------------------------- /client/lib/slsa_test.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestCheckForDowngrade(t *testing.T) { 10 | require.NoError(t, checkForDowngrade("v0.100", "v0.100")) 11 | require.NoError(t, checkForDowngrade("v0.100", "v0.101")) 12 | require.NoError(t, checkForDowngrade("v0.100", "v0.200")) 13 | require.NoError(t, checkForDowngrade("v0.100", "v1.0")) 14 | require.NoError(t, checkForDowngrade("v0.1", "v1.0")) 15 | require.NoError(t, checkForDowngrade("v1.0", "v1.1")) 16 | require.Equal(t, "failed to update because the new version (\"v0.99\") is a downgrade compared to the current version (\"v0.100\")", 17 | checkForDowngrade("v0.100", "v0.99").Error()) 18 | require.Equal(t, "failed to update because the new version (\"v0.10\") is a downgrade compared to the current version (\"v0.100\")", 19 | checkForDowngrade("v0.100", "v0.10").Error()) 20 | require.Equal(t, "failed to update because the new version (\"v0.100\") is a downgrade compared to the current version (\"v1.0\")", 21 | checkForDowngrade("v1.0", "v0.100").Error()) 22 | } 23 | -------------------------------------------------------------------------------- /client/table/table_test.go: -------------------------------------------------------------------------------- 1 | // Forked from https://github.com/charmbracelet/bubbles/blob/master/table/table_test.go to add horizontal scrolling 2 | 3 | package table 4 | 5 | import ( 6 | "testing" 7 | 8 | "github.com/ddworken/hishtory/shared/testutils" 9 | ) 10 | 11 | func TestFromValues(t *testing.T) { 12 | input := "foo1,bar1\nfoo2,bar2\nfoo3,bar3" 13 | table := New(WithColumns([]Column{{Title: "Foo"}, {Title: "Bar"}})) 14 | table.FromValues(input, ",") 15 | 16 | if len(table.rows) != 3 { 17 | t.Fatalf("expect table to have 3 rows but it has %d", len(table.rows)) 18 | } 19 | 20 | expect := []Row{ 21 | {"foo1", "bar1"}, 22 | {"foo2", "bar2"}, 23 | {"foo3", "bar3"}, 24 | } 25 | if !deepEqual(table.rows, expect) { 26 | t.Fatal("table rows is not equals to the input") 27 | } 28 | } 29 | 30 | func TestFromValuesWithTabSeparator(t *testing.T) { 31 | input := "foo1.\tbar1\nfoo,bar,baz\tbar,2" 32 | table := New(WithColumns([]Column{{Title: "Foo"}, {Title: "Bar"}})) 33 | table.FromValues(input, "\t") 34 | 35 | if len(table.rows) != 2 { 36 | t.Fatalf("expect table to have 2 rows but it has %d", len(table.rows)) 37 | } 38 | 39 | expect := []Row{ 40 | {"foo1.", "bar1"}, 41 | {"foo,bar,baz", "bar,2"}, 42 | } 43 | if !deepEqual(table.rows, expect) { 44 | t.Fatal("table rows is not equals to the input") 45 | } 46 | } 47 | 48 | func TestHScoll(t *testing.T) { 49 | table := New( 50 | WithColumns([]Column{{Title: "Column1", Width: 10}, {Title: "Column2", Width: 20}}), 51 | WithRows([]Row{ 52 | {"a1", "a2345"}, 53 | {"b1", "b23"}, 54 | {"c1", "c1234567890abcdefghijklmnopqrstuvwxyz"}, 55 | }), 56 | ) 57 | testutils.CompareGoldens(t, table.View(), "unittestTable-truncatedTable") 58 | table.MoveRight(1) 59 | testutils.CompareGoldens(t, table.View(), "unittestTable-truncatedTable-right1") 60 | table.MoveRight(1) 61 | testutils.CompareGoldens(t, table.View(), "unittestTable-truncatedTable-right2") 62 | table.MoveRight(1) 63 | testutils.CompareGoldens(t, table.View(), "unittestTable-truncatedTable-right3") 64 | table.MoveLeft(1) 65 | testutils.CompareGoldens(t, table.View(), "unittestTable-truncatedTable-right2") 66 | } 67 | 68 | func deepEqual(a, b []Row) bool { 69 | if len(a) != len(b) { 70 | return false 71 | } 72 | for i, r := range a { 73 | for j, f := range r { 74 | if f != b[i][j] { 75 | return false 76 | } 77 | } 78 | } 79 | return true 80 | } 81 | -------------------------------------------------------------------------------- /client/testdata/TestBashOrderingBug-Export: -------------------------------------------------------------------------------- 1 | command1 2 | command2 3 | command3 4 | command4 5 | -------------------------------------------------------------------------------- /client/testdata/TestChangeSyncingStatus-Offline: -------------------------------------------------------------------------------- 1 | hiSHtory: v0.Unknown 2 | Enabled: true 3 | Sync Mode: Disabled 4 | Commit Hash: Unknown 5 | -------------------------------------------------------------------------------- /client/testdata/TestChangeSyncingStatus-Online: -------------------------------------------------------------------------------- 1 | hiSHtory: v0.Unknown 2 | Enabled: true 3 | Sync Mode: Enabled 4 | Sync Server: http://localhost:8080 5 | Sync Status: Synced 6 | Commit Hash: Unknown 7 | -------------------------------------------------------------------------------- /client/testdata/TestDefaultSearchColumns-Default-Echo: -------------------------------------------------------------------------------- 1 | echo hi 2 | ls 3 | -------------------------------------------------------------------------------- /client/testdata/TestDefaultSearchColumns-Default-Hi: -------------------------------------------------------------------------------- 1 | echo hi 2 | ls 3 | -------------------------------------------------------------------------------- /client/testdata/TestDefaultSearchColumns-MyCol-bar: -------------------------------------------------------------------------------- 1 | ls 2 | -------------------------------------------------------------------------------- /client/testdata/TestDefaultSearchColumns-MyCol-baz: -------------------------------------------------------------------------------- 1 | echo hi 2 | -------------------------------------------------------------------------------- /client/testdata/TestDefaultSearchColumns-NoCWD-Echo: -------------------------------------------------------------------------------- 1 | echo hi 2 | -------------------------------------------------------------------------------- /client/testdata/TestDefaultSearchColumns-NoCWD-Hi: -------------------------------------------------------------------------------- 1 | echo hi 2 | ls 3 | -------------------------------------------------------------------------------- /client/testdata/TestDefaultSearchColumns-NoCWDHostname-Echo: -------------------------------------------------------------------------------- 1 | echo hi 2 | -------------------------------------------------------------------------------- /client/testdata/TestDefaultSearchColumns-NoCWDHostname-Hi: -------------------------------------------------------------------------------- 1 | echo hi 2 | -------------------------------------------------------------------------------- /client/testdata/TestExportJson: -------------------------------------------------------------------------------- 1 | {"command":"echo synth2","current_working_directory":"/tmp/","custom_columns":null,"end_time":"2022-10-18T04:43:24Z","exit_code":2,"home_directory":"/home/david/","hostname":"localhost","local_username":"david","start_time":"2022-10-18T04:43:21Z"} 2 | {"command":"echo synth1","current_working_directory":"/tmp/","custom_columns":[{"name":"MyCol","value":"bar"}],"end_time":"2022-10-18T04:43:19Z","exit_code":2,"home_directory":"/home/david/","hostname":"localhost","local_username":"david","start_time":"1970-01-14T22:56:07-08:00"} 3 | -------------------------------------------------------------------------------- /client/testdata/TestFish-table: -------------------------------------------------------------------------------- 1 | CWD Hostname Exit Code Command 2 | / ghaction-runner-hostname 0 hishtory config-set displayed-columns CWD Hostname 'Exit Code' Command 3 | / ghaction-runner-hostname 0 exit 4 | / ghaction-runner-hostname 0 echo foo 5 | / ghaction-runner-hostname 0 fish 6 | / ghaction-runner-hostname 0 ls /tmp/ & 7 | / ghaction-runner-hostname 0 echo "foo" 8 | / ghaction-runner-hostname 0 echo bar 9 | / ghaction-runner-hostname 0 echo foo 10 | -------------------------------------------------------------------------------- /client/testdata/TestInstallSkipConfigModification-InstallOutput-darwin: -------------------------------------------------------------------------------- 1 | Please edit "~/.bashrc" to add: 2 | 3 | ``` 4 | # Hishtory Config: 5 | export PATH="$PATH:/Users/runner/.hishtory" 6 | source /Users/runner/.hishtory/config.sh 7 | ``` 8 | 9 | Please edit "~/.bash_profile" to add: 10 | 11 | ``` 12 | # Hishtory Config: 13 | export PATH="$PATH:/Users/runner/.hishtory" 14 | source /Users/runner/.hishtory/config.sh 15 | ``` 16 | 17 | Please edit "~/.zshrc" to add: 18 | 19 | ``` 20 | # Hishtory Config: 21 | export PATH="$PATH:/Users/runner/.hishtory" 22 | source /Users/runner/.hishtory/config.zsh 23 | ``` 24 | 25 | -------------------------------------------------------------------------------- /client/testdata/TestInstallSkipConfigModification-InstallOutput-linux: -------------------------------------------------------------------------------- 1 | Please edit "~/.bashrc" to add: 2 | 3 | ``` 4 | # Hishtory Config: 5 | export PATH="$PATH:/home/runner/.hishtory" 6 | source /home/runner/.hishtory/config.sh 7 | ``` 8 | 9 | Please edit "~/.bash_profile" to add: 10 | 11 | ``` 12 | # Hishtory Config: 13 | export PATH="$PATH:/home/runner/.hishtory" 14 | source /home/runner/.hishtory/config.sh 15 | ``` 16 | 17 | Please edit "~/.zshrc" to add: 18 | 19 | ``` 20 | # Hishtory Config: 21 | export PATH="$PATH:/home/runner/.hishtory" 22 | source /home/runner/.hishtory/config.zsh 23 | ``` 24 | 25 | -------------------------------------------------------------------------------- /client/testdata/TestSortByConsistentTimezone-export: -------------------------------------------------------------------------------- 1 | first_entry 2 | second_entry 3 | third_entry 4 | -------------------------------------------------------------------------------- /client/testdata/TestSortByConsistentTimezone-query: -------------------------------------------------------------------------------- 1 | Hostname CWD Timestamp Runtime Exit Code Command 2 | localhost /tmp/ Apr 16 2022 01:36:26 PDT 1s 2 third_entry 3 | localhost /tmp/ Apr 16 2022 01:19:46 PDT 1s 2 second_entry 4 | localhost /tmp/ Apr 16 2022 01:03:06 PDT 1s 2 first_entry 5 | -------------------------------------------------------------------------------- /client/testdata/TestStatusFullConfig: -------------------------------------------------------------------------------- 1 | hiSHtory: v0.Unknown 2 | Enabled: true 3 | Commit Hash: Unknown 4 | Full Config: 5 | controlrsearchenabled: true 6 | displayedcolumns: 7 | - Hostname 8 | - CWD 9 | - Timestamp 10 | - Runtime 11 | - Exit Code 12 | - Command 13 | customcolumns: [] 14 | forcecompactmode: false 15 | isoffline: false 16 | filterduplicatecommands: false 17 | timestampformat: Jan 2 2006 15:04:05 MST 18 | betamode: false 19 | highlightmatches: true 20 | aicompletion: true 21 | enablepresaving: true 22 | colorscheme: 23 | selectedtext: '#ffff99' 24 | selectedbackground: '#3300ff' 25 | bordercolor: '#585858' 26 | defaultfilter: "" 27 | aicompletionendpoint: https://api.openai.com/v1/chat/completions 28 | keybindings: 29 | up: 30 | - up 31 | - alt+OA 32 | - ctrl+p 33 | down: 34 | - down 35 | - alt+OB 36 | - ctrl+n 37 | pageup: 38 | - pgup 39 | pagedown: 40 | - pgdown 41 | selectentry: 42 | - enter 43 | selectentryandchangedir: 44 | - ctrl+x 45 | left: 46 | - left 47 | right: 48 | - right 49 | tableleft: 50 | - shift+left 51 | tableright: 52 | - shift+right 53 | deleteentry: 54 | - ctrl+k 55 | help: 56 | - ctrl+h 57 | quit: 58 | - esc 59 | - ctrl+c 60 | - ctrl+d 61 | jumpstartofinput: 62 | - ctrl+a 63 | jumpendofinput: 64 | - ctrl+e 65 | wordleft: 66 | - ctrl+left 67 | wordright: 68 | - ctrl+right 69 | loglevel: info 70 | fullscreenrendering: false 71 | defaultsearchcolumns: 72 | - command 73 | - hostname 74 | - current_working_directory 75 | 76 | -------------------------------------------------------------------------------- /client/testdata/TestTimestampFormat-query: -------------------------------------------------------------------------------- 1 | Hostname CWD Timestamp Runtime Exit Code Command 2 | localhost ~/foo/ 2022/Apr/16 01:03 24s 3 table_cmd2 3 | localhost /tmp/ 2022/Apr/16 01:03 4s 2 table_cmd1 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 4 | -------------------------------------------------------------------------------- /client/testdata/TestTui-AiQuery: -------------------------------------------------------------------------------- 1 | Search Query: > ?myQuery 2 | 3 | ┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 4 | │ Hostname CWD Timestamp Runtime Exit Code Command │ 5 | │────────────────────────────────────────────────────────────────────────────────────────────────────────│ 6 | │ OpenAI N/A N/A N/A 0 result 1 │ 7 | │ OpenAI N/A N/A N/A 0 result 2 │ 8 | │ OpenAI N/A N/A N/A 0 longer result 3 │ 9 | │ │ 10 | │ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | └────────────────────────────────────────────────────────────────────────────────────────────────────────┘ 27 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/TestTui-AiQuery-Disabled: -------------------------------------------------------------------------------- 1 | Search Query: > ?myQuery 2 | 3 | ┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 4 | │ Hostname CWD Timestamp Runtime Exit Code Command │ 5 | │────────────────────────────────────────────────────────────────────────────────────────────────────────│ 6 | │ │ 7 | │ │ 8 | │ │ 9 | │ │ 10 | │ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | └────────────────────────────────────────────────────────────────────────────────────────────────────────┘ 27 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/TestTui-ColoredOutput-darwin-23: -------------------------------------------------------------------------------- 1 | Search Query: > ls 2 | 3 | ┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 4 | │ Hostname CWD Timestamp Runtime Exit Code Command │ 5 | │────────────────────────────────────────────────────────────────────────────────────────────────────────│ 6 | │ localhost /tmp/ Oct 17 2022 21:43:21 PDT 3s 2 echo 'aaaaaa bbbb' │ 7 | │ localhost /tmp/ Oct 17 2022 21:43:16 PDT 3s 2 ls ~/ │ 8 | │ │ 9 | │ │ 10 | │ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | └────────────────────────────────────────────────────────────────────────────────────────────────────────┘ 27 | hiSHtory: Search your shell history  • ctrl+h help  -------------------------------------------------------------------------------- /client/testdata/TestTui-ColoredOutput-linux-actions: -------------------------------------------------------------------------------- 1 | Search Query: > ls 2 | 3 | ┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 4 | │ Hostname CWD Timestamp Runtime Exit Code Command │ 5 | │────────────────────────────────────────────────────────────────────────────────────────────────────────│ 6 | │ localhost /tmp/ Oct 17 2022 21:43:21 PDT 3s 2 echo 'aaaaaa bbbb' │ 7 | │ localhost /tmp/ Oct 17 2022 21:43:16 PDT 3s 2 ls ~/ │ 8 | │ │ 9 | │ │ 10 | │ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | └────────────────────────────────────────────────────────────────────────────────────────────────────────┘ 27 | hiSHtory: Search your shell history  • ctrl+h help  -------------------------------------------------------------------------------- /client/testdata/TestTui-DefaultColorScheme: -------------------------------------------------------------------------------- 1 | selected-text: #ffff99 2 | selected-background: #3300ff 3 | border-color: #585858 4 | -------------------------------------------------------------------------------- /client/testdata/TestTui-DefaultFilter-Deleted: -------------------------------------------------------------------------------- 1 | Search Query: 2 | 3 | ┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 4 | │ Hostname CWD Timestamp Runtime Exit Code Command │ 5 | │────────────────────────────────────────────────────────────────────────────────────────────────────────│ 6 | │ localhost /tmp/ Oct 17 2022 21:43:31 PDT 3s 1 exit 1 │ 7 | │ localhost /tmp/ Oct 17 2022 21:43:26 PDT 3s 0 exit 0 │ 8 | │ localhost /tmp/ Oct 17 2022 21:43:21 PDT 3s 2 echo 'aaaaaa bbbb' │ 9 | │ localhost /tmp/ Oct 17 2022 21:43:16 PDT 3s 2 ls ~/ │ 10 | │ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | └────────────────────────────────────────────────────────────────────────────────────────────────────────┘ 27 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/TestTui-DefaultFilter-DeletedWithText: -------------------------------------------------------------------------------- 1 | Search Query: exit 2 | 3 | ┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 4 | │ Hostname CWD Timestamp Runtime Exit Code Command │ 5 | │────────────────────────────────────────────────────────────────────────────────────────────────────────│ 6 | │ localhost /tmp/ Oct 17 2022 21:43:31 PDT 3s 1 exit 1 │ 7 | │ localhost /tmp/ Oct 17 2022 21:43:26 PDT 3s 0 exit 0 │ 8 | │ │ 9 | │ │ 10 | │ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | └────────────────────────────────────────────────────────────────────────────────────────────────────────┘ 27 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/TestTui-DefaultFilter-Enabled: -------------------------------------------------------------------------------- 1 | Search Query: [exit_code:0] 2 | 3 | ┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 4 | │ Hostname CWD Timestamp Runtime Exit Code Command │ 5 | │────────────────────────────────────────────────────────────────────────────────────────────────────────│ 6 | │ localhost /tmp/ Oct 17 2022 21:43:26 PDT 3s 0 exit 0 │ 7 | │ │ 8 | │ │ 9 | │ │ 10 | │ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | └────────────────────────────────────────────────────────────────────────────────────────────────────────┘ 27 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/TestTui-DefaultFilter-EnabledAdditionalQuery: -------------------------------------------------------------------------------- 1 | Search Query: [exit_code:0] exit 2 | 3 | ┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 4 | │ Hostname CWD Timestamp Runtime Exit Code Command │ 5 | │────────────────────────────────────────────────────────────────────────────────────────────────────────│ 6 | │ localhost /tmp/ Oct 17 2022 21:43:26 PDT 3s 0 exit 0 │ 7 | │ │ 8 | │ │ 9 | │ │ 10 | │ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | └────────────────────────────────────────────────────────────────────────────────────────────────────────┘ 27 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/TestTui-DeleteAgainStill: -------------------------------------------------------------------------------- 1 | Search Query: > ls 2 | 3 | ┌───────────────────────────────────────────────────────────────────────────────────────────┐ 4 | │ Hostname CWD Timestamp Runtime Exit Code Command │ 5 | │───────────────────────────────────────────────────────────────────────────────────────────│ 6 | │ localhost /tmp/ Oct 17 2022 21:43:16 PDT 3s 2 ls ~/ │ 7 | │ │ 8 | │ │ 9 | │ │ 10 | │ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | └───────────────────────────────────────────────────────────────────────────────────────────┘ 27 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/TestTui-Escaping: -------------------------------------------------------------------------------- 1 | Search Query: > ls 2 | 3 | ┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 4 | │ Hostname CWD Timestamp Runtime Exit Code Command │ 5 | │────────────────────────────────────────────────────────────────────────────────────────────────────────│ 6 | │ localhost /tmp/ Oct 17 2022 21:43:26 PDT 3s 2 "echo 'a\tb\nc'" │ 7 | │ localhost /tmp/ Oct 17 2022 21:43:21 PDT 3s 2 echo 'aaaaaa bbbb' │ 8 | │ localhost /tmp/ Oct 17 2022 21:43:16 PDT 3s 2 ls ~/ │ 9 | │ │ 10 | │ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | └────────────────────────────────────────────────────────────────────────────────────────────────────────┘ 27 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/TestTui-Exit-darwin: -------------------------------------------------------------------------------- 1 | runner@ghaction-runner-hostname hishtory % hishtory tquery 2 | 3 | runner@ghaction-runner-hostname hishtory % -------------------------------------------------------------------------------- /client/testdata/TestTui-Exit-linux: -------------------------------------------------------------------------------- 1 | ghaction-runner-hostname% hishtory tquery 2 | 3 | ghaction-runner-hostname% -------------------------------------------------------------------------------- /client/testdata/TestTui-ExportWithAdditionalEntries: -------------------------------------------------------------------------------- 1 | ls ~/ 2 | echo 'aaaaaa bbbb' 3 | for i in 1 4 | for i in 2 5 | i for in 6 | -------------------------------------------------------------------------------- /client/testdata/TestTui-ExportWithEvenMoreEntries: -------------------------------------------------------------------------------- 1 | ls ~/ 2 | echo 'aaaaaa bbbb' 3 | for i in 1 4 | for i in 2 5 | i for in 6 | foo:bar 7 | -------------------------------------------------------------------------------- /client/testdata/TestTui-HelpPageClosed: -------------------------------------------------------------------------------- 1 | Search Query: > ls 2 | 3 | ┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 4 | │ Hostname CWD Timestamp Runtime Exit Code Command │ 5 | │────────────────────────────────────────────────────────────────────────────────────────────────────────│ 6 | │ localhost /tmp/ Oct 17 2022 21:43:21 PDT 3s 2 echo 'aaaaaa bbbb' │ 7 | │ localhost /tmp/ Oct 17 2022 21:43:16 PDT 3s 2 ls ~/ │ 8 | │ │ 9 | │ │ 10 | │ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | └────────────────────────────────────────────────────────────────────────────────────────────────────────┘ 27 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/TestTui-Initial: -------------------------------------------------------------------------------- 1 | Search Query: > ls 2 | 3 | ┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 4 | │ Hostname CWD Timestamp Runtime Exit Code Command │ 5 | │────────────────────────────────────────────────────────────────────────────────────────────────────────│ 6 | │ localhost /tmp/ Oct 17 2022 21:43:21 PDT 3s 2 echo 'aaaaaa bbbb' │ 7 | │ localhost /tmp/ Oct 17 2022 21:43:16 PDT 3s 2 ls ~/ │ 8 | │ │ 9 | │ │ 10 | │ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | └────────────────────────────────────────────────────────────────────────────────────────────────────────┘ 27 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/TestTui-InitialInvalidSearch: -------------------------------------------------------------------------------- 1 | Search Query: > ls 2 | 3 | ┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 4 | │ Hostname CWD Timestamp Runtime Exit Code Command │ 5 | │────────────────────────────────────────────────────────────────────────────────────────────────────────│ 6 | │ localhost /tmp/ Oct 17 2022 21:43:16 PDT 3s 2 ls ~/ │ 7 | │ │ 8 | │ │ 9 | │ │ 10 | │ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | └────────────────────────────────────────────────────────────────────────────────────────────────────────┘ 27 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/TestTui-InvalidSearch: -------------------------------------------------------------------------------- 1 | Warning: failed to search: search query contains unknown search atom 'ls' that doesn't match any column names 2 | 3 | Search Query: > ls: 4 | 5 | ┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 6 | │ Hostname CWD Timestamp Runtime Exit Code Command │ 7 | │────────────────────────────────────────────────────────────────────────────────────────────────────────│ 8 | │ localhost /tmp/ Oct 17 2022 21:43:16 PDT 3s 2 ls ~/ │ 9 | │ │ 10 | │ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | │ │ 27 | │ │ 28 | └────────────────────────────────────────────────────────────────────────────────────────────────────────┘ 29 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/TestTui-InvalidSearchBecomesValid: -------------------------------------------------------------------------------- 1 | Search Query: > ls 2 | 3 | ┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 4 | │ Hostname CWD Timestamp Runtime Exit Code Command │ 5 | │────────────────────────────────────────────────────────────────────────────────────────────────────────│ 6 | │ localhost /tmp/ Oct 17 2022 21:43:16 PDT 3s 2 ls ~/ │ 7 | │ │ 8 | │ │ 9 | │ │ 10 | │ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | └────────────────────────────────────────────────────────────────────────────────────────────────────────┘ 27 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/TestTui-JumpCursor: -------------------------------------------------------------------------------- 1 | Search Query: > AAA foo ZZZ -------------------------------------------------------------------------------- /client/testdata/TestTui-KeyBindings-Configured: -------------------------------------------------------------------------------- 1 | up: up alt+OA ctrl+p 2 | down: ? 3 | page-up: pgup 4 | page-down: pgdown 5 | select-entry: enter 6 | select-entry-and-cd: ctrl+x 7 | left: left 8 | right: right 9 | table-left: shift+left 10 | table-right: shift+right 11 | delete-entry: ctrl+k 12 | help: ctrl+j 13 | quit: esc ctrl+c ctrl+d 14 | jump-start-of-input: ctrl+a 15 | jump-end-of-input: ctrl+e 16 | word-left: ctrl+left 17 | word-right: ctrl+right 18 | -------------------------------------------------------------------------------- /client/testdata/TestTui-KeyBindings-Default: -------------------------------------------------------------------------------- 1 | up: up alt+OA ctrl+p 2 | down: down alt+OB ctrl+n 3 | page-up: pgup 4 | page-down: pgdown 5 | select-entry: enter 6 | select-entry-and-cd: ctrl+x 7 | left: left 8 | right: right 9 | table-left: shift+left 10 | table-right: shift+right 11 | delete-entry: ctrl+k 12 | help: ctrl+h 13 | quit: esc ctrl+c ctrl+d 14 | jump-start-of-input: ctrl+a 15 | jump-end-of-input: ctrl+e 16 | word-left: ctrl+left 17 | word-right: ctrl+right 18 | -------------------------------------------------------------------------------- /client/testdata/TestTui-LeftScroll: -------------------------------------------------------------------------------- 1 | Search Query: > ls 2 | 3 | ┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 4 | │ Hostname CWD Timestamp Runtime Exit Code Command │ 5 | │────────────────────────────────────────────────────────────────────────────────────────────────────────│ 6 | │ localhost /tmp/ Oct 17 2022 21:43:16 PDT 3s 2 ls ~/ │ 7 | │ │ 8 | │ │ 9 | │ │ 10 | │ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | └────────────────────────────────────────────────────────────────────────────────────────────────────────┘ 27 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/TestTui-LongQuery: -------------------------------------------------------------------------------- 1 | Search Query: > 1234567890qwertyuip1234567890qwertyuip1234567890qwertyuip1234567890qwertyuip12345678 2 | ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ 3 | │ Hostname CWD Timestamp Runtime Exit Code Command │ 4 | │──────────────────────────────────────────────────────────────────────────────────────────────────│ 5 | │ │ 6 | │ │ 7 | │ │ 8 | │ │ 9 | │ │ 10 | │ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | └──────────────────────────────────────────────────────────────────────────────────────────────────┘ 16 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/TestTui-Offline: -------------------------------------------------------------------------------- 1 | Warning: failed to contact the hishtory backend (are you offline?), so some results may be stale 2 | 3 | Search Query: > ls 4 | 5 | ┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 6 | │ Hostname CWD Timestamp Runtime Exit Code Command │ 7 | │────────────────────────────────────────────────────────────────────────────────────────────────────────│ 8 | │ localhost /tmp/ Oct 17 2022 21:43:21 PDT 3s 2 echo 'aaaaaa bbbb' │ 9 | │ localhost /tmp/ Oct 17 2022 21:43:16 PDT 3s 2 ls ~/ │ 10 | │ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | │ │ 27 | │ │ 28 | └────────────────────────────────────────────────────────────────────────────────────────────────────────┘ 29 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/TestTui-OfflineInvalid: -------------------------------------------------------------------------------- 1 | Warning: failed to contact the hishtory backend (are you offline?), so some results may be stale 2 | Warning: failed to search: search query contains unknown search atom 'ls' that doesn't match any column names 3 | 4 | Search Query: > ls: 5 | 6 | ┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 7 | │ Hostname CWD Timestamp Runtime Exit Code Command │ 8 | │────────────────────────────────────────────────────────────────────────────────────────────────────────│ 9 | │ localhost /tmp/ Oct 17 2022 21:43:16 PDT 3s 2 ls ~/ │ 10 | │ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | │ │ 27 | │ │ 28 | │ │ 29 | └────────────────────────────────────────────────────────────────────────────────────────────────────────┘ 30 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/TestTui-Search: -------------------------------------------------------------------------------- 1 | Search Query: > ls 2 | 3 | ┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 4 | │ Hostname CWD Timestamp Runtime Exit Code Command │ 5 | │────────────────────────────────────────────────────────────────────────────────────────────────────────│ 6 | │ localhost /tmp/ Oct 17 2022 21:43:16 PDT 3s 2 ls ~/ │ 7 | │ │ 8 | │ │ 9 | │ │ 10 | │ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | └────────────────────────────────────────────────────────────────────────────────────────────────────────┘ 27 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/TestTui-SearchBackslash: -------------------------------------------------------------------------------- 1 | Search Query: > for\ i\ in 2 | 3 | ┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 4 | │ Hostname CWD Timestamp Runtime Exit Code Command │ 5 | │────────────────────────────────────────────────────────────────────────────────────────────────────────│ 6 | │ localhost /tmp/ Oct 17 2022 21:43:31 PDT 3s 2 for i in 2 │ 7 | │ localhost /tmp/ Oct 17 2022 21:43:26 PDT 3s 2 for i in 1 │ 8 | │ │ 9 | │ │ 10 | │ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | └────────────────────────────────────────────────────────────────────────────────────────────────────────┘ 27 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/TestTui-SearchColonDoubleQuoted: -------------------------------------------------------------------------------- 1 | Search Query: > "foo:bar" 2 | 3 | ┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 4 | │ Hostname CWD Timestamp Runtime Exit Code Command │ 5 | │────────────────────────────────────────────────────────────────────────────────────────────────────────│ 6 | │ localhost /tmp/ Oct 17 2022 21:43:41 PDT 3s 2 foo:bar │ 7 | │ │ 8 | │ │ 9 | │ │ 10 | │ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | └────────────────────────────────────────────────────────────────────────────────────────────────────────┘ 27 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/TestTui-SearchColonError: -------------------------------------------------------------------------------- 1 | Warning: failed to search: search query contains unknown search atom 'foo' that doesn't match any column names 2 | 3 | Search Query: > foo:bar 4 | 5 | ┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 6 | │ Hostname CWD Timestamp Runtime Exit Code Command │ 7 | │────────────────────────────────────────────────────────────────────────────────────────────────────────│ 8 | │ localhost /tmp/ Oct 17 2022 21:43:41 PDT 3s 2 foo:bar │ 9 | │ │ 10 | │ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | │ │ 27 | │ │ 28 | └────────────────────────────────────────────────────────────────────────────────────────────────────────┘ 29 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/TestTui-SearchColonEscaped: -------------------------------------------------------------------------------- 1 | Search Query: > foo\:bar 2 | 3 | ┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 4 | │ Hostname CWD Timestamp Runtime Exit Code Command │ 5 | │────────────────────────────────────────────────────────────────────────────────────────────────────────│ 6 | │ localhost /tmp/ Oct 17 2022 21:43:41 PDT 3s 2 foo:bar │ 7 | │ │ 8 | │ │ 9 | │ │ 10 | │ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | └────────────────────────────────────────────────────────────────────────────────────────────────────────┘ 27 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/TestTui-SearchQuoteDash: -------------------------------------------------------------------------------- 1 | Search Query: > "--bar" 2 | 3 | ┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 4 | │ Hostname CWD Timestamp Runtime Exit Code Command │ 5 | │────────────────────────────────────────────────────────────────────────────────────────────────────────│ 6 | │ localhost /tmp/ Oct 17 2022 21:43:46 PDT 3s 2 foo --bar │ 7 | │ │ 8 | │ │ 9 | │ │ 10 | │ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | └────────────────────────────────────────────────────────────────────────────────────────────────────────┘ 27 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/TestTui-SearchQuoted: -------------------------------------------------------------------------------- 1 | Search Query: > "for i in" 2 | 3 | ┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 4 | │ Hostname CWD Timestamp Runtime Exit Code Command │ 5 | │────────────────────────────────────────────────────────────────────────────────────────────────────────│ 6 | │ localhost /tmp/ Oct 17 2022 21:43:31 PDT 3s 2 for i in 2 │ 7 | │ localhost /tmp/ Oct 17 2022 21:43:26 PDT 3s 2 for i in 1 │ 8 | │ │ 9 | │ │ 10 | │ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | └────────────────────────────────────────────────────────────────────────────────────────────────────────┘ 27 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/TestTui-SearchUnquoted: -------------------------------------------------------------------------------- 1 | Search Query: > for i in 2 | 3 | ┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 4 | │ Hostname CWD Timestamp Runtime Exit Code Command │ 5 | │────────────────────────────────────────────────────────────────────────────────────────────────────────│ 6 | │ localhost /tmp/ Oct 17 2022 21:43:36 PDT 3s 2 i for in │ 7 | │ localhost /tmp/ Oct 17 2022 21:43:31 PDT 3s 2 for i in 2 │ 8 | │ localhost /tmp/ Oct 17 2022 21:43:26 PDT 3s 2 for i in 1 │ 9 | │ │ 10 | │ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | └────────────────────────────────────────────────────────────────────────────────────────────────────────┘ 27 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/TestTui-SelectAndCd: -------------------------------------------------------------------------------- 1 | cd "/tmp/" && echo 'aaaaaa bbbb' -------------------------------------------------------------------------------- /client/testdata/TestTui-SmallTerminal: -------------------------------------------------------------------------------- 1 | Search Query: > ls 2 | ┌────────────────────────────────────────────────────────────────────────────────────────────┐ 3 | │ Hostname CWD Timestamp Runtime Exit Code Command │ 4 | │────────────────────────────────────────────────────────────────────────────────────────────│ 5 | │ localhost /tmp/ Oct 17 2022 21:43:21 PDT 3s 2 echo 'aaaaaa bbbb' │ 6 | │ localhost /tmp/ Oct 17 2022 21:43:16 PDT 3s 2 ls ~/ │ 7 | │ │ 8 | │ │ 9 | │ │ 10 | │ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | └────────────────────────────────────────────────────────────────────────────────────────────┘ 16 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/TestTui-TiniestTerminal: -------------------------------------------------------------------------------- 1 | Search Query: > ls 2 | ┌────────────────────────────────────────────────────────────────────────────────────────────┐ 3 | │ Hostname CWD Timestamp Runtime Exit Code Command │ 4 | │────────────────────────────────────────────────────────────────────────────────────────────│ 5 | │ localhost /tmp/ Oct 17 2022 21:43:21 PDT 3s 2 echo 'aaaaaa bbbb' │ 6 | │ localhost /tmp/ Oct 17 2022 21:43:16 PDT 3s 2 ls ~/ │ 7 | │ │ 8 | │ │ 9 | └────────────────────────────────────────────────────────────────────────────────────────────┘ -------------------------------------------------------------------------------- /client/testdata/TestTui-TinyTerminal: -------------------------------------------------------------------------------- 1 | Search Query: > ls 2 | ┌────────────────────────────────────────────────────────────────────────────────────────────┐ 3 | │ Hostname CWD Timestamp Runtime Exit Code Command │ 4 | │────────────────────────────────────────────────────────────────────────────────────────────│ 5 | │ localhost /tmp/ Oct 17 2022 21:43:21 PDT 3s 2 echo 'aaaaaa bbbb' │ 6 | │ localhost /tmp/ Oct 17 2022 21:43:16 PDT 3s 2 ls ~/ │ 7 | │ │ 8 | │ │ 9 | │ │ 10 | └────────────────────────────────────────────────────────────────────────────────────────────┘ 11 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/TestTui-TinyTerminalHelp: -------------------------------------------------------------------------------- 1 | Search Query: > ls 2 | ┌────────────────────────────────────────────────────────────────────────────────────────────┐ 3 | │ Hostname CWD Timestamp Runtime Exit Code Command │ 4 | │────────────────────────────────────────────────────────────────────────────────────────────│ 5 | │ localhost /tmp/ Oct 17 2022 21:43:21 PDT 3s 2 echo 'aaaaaa bbbb' │ 6 | │ localhost /tmp/ Oct 17 2022 21:43:16 PDT 3s 2 ls ~/ │ 7 | hiSHtory: Search your shell history 8 | ↑ scroll up 9 | ← move left 10 | enter select an entry 11 | ctrl+x select an entry and cd into that directory -------------------------------------------------------------------------------- /client/testdata/TestTuiBench-Query: -------------------------------------------------------------------------------- 1 | Search Query: > this is 123 2 | ┌──────────────────────────────────────────────────────────────────────────────────────────────┐ 3 | │ Hostname CWD Timestamp Runtime Exit Code Command │ 4 | │──────────────────────────────────────────────────────────────────────────────────────────────│ 5 | │ localhost /tmp/ Oct 17 2022 21:53:41 P… 3s 2 this is a long co… │ 6 | │ │ 7 | │ │ 8 | │ │ 9 | │ │ 10 | │ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | └──────────────────────────────────────────────────────────────────────────────────────────────┘ 16 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/testControlR-AdvancedSearch: -------------------------------------------------------------------------------- 1 | Search Query: > cwd:/tmp/ ls 2 | 3 | ┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 4 | │ Hostname CWD Timestamp Runtime Exit Code Command │ 5 | │────────────────────────────────────────────────────────────────────────────────────────────────────────│ 6 | │ localhost /tmp/ Oct 17 2022 21:43:26 PDT 3s 2 ls ~/bar/ │ 7 | │ localhost /tmp/ Oct 17 2022 21:43:21 PDT 3s 2 ls ~/foo/ │ 8 | │ │ 9 | │ │ 10 | │ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | └────────────────────────────────────────────────────────────────────────────────────────────────────────┘ 27 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/testControlR-ControlC-bash-darwin: -------------------------------------------------------------------------------- 1 | bash-5.2$ source /Users/runner/.bashrc 2 | bash-5.2$ echo -------------------------------------------------------------------------------- /client/testdata/testControlR-ControlC-bash-linux: -------------------------------------------------------------------------------- 1 | runner@ghaction-runner-hostname:~/work/hishtory/hishtory$ source /home/runner/.bashrc 2 | runner@ghaction-runner-hostname:~/work/hishtory/hishtory$ echo -------------------------------------------------------------------------------- /client/testdata/testControlR-ControlC-zsh-darwin: -------------------------------------------------------------------------------- 1 | runner@ghaction-runner-hostname hishtory % echo -------------------------------------------------------------------------------- /client/testdata/testControlR-ControlC-zsh-linux: -------------------------------------------------------------------------------- 1 | ghaction-runner-hostname% echo -------------------------------------------------------------------------------- /client/testdata/testControlR-DisplayMultiline-bash: -------------------------------------------------------------------------------- 1 | Search Query: > Slah 2 | 3 | ┌─────────────────────────────────────────────────────────────────────────────┐ 4 | │ Hostname Exit Code Command foo │ 5 | │─────────────────────────────────────────────────────────────────────────────│ 6 | │ ghaction-runner-hostname 0 ls -Slah / foo │ 7 | │ │ 8 | │ │ 9 | │ │ 10 | │ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | └─────────────────────────────────────────────────────────────────────────────┘ 27 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/testControlR-DisplayMultiline-fish: -------------------------------------------------------------------------------- 1 | Search Query: > Slah 2 | 3 | ┌─────────────────────────────────────────────────────────────────────────────┐ 4 | │ Hostname Exit Code Command foo │ 5 | │─────────────────────────────────────────────────────────────────────────────│ 6 | │ ghaction-runner-hostname 0 ls -Slah / foo │ 7 | │ │ 8 | │ │ 9 | │ │ 10 | │ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | └─────────────────────────────────────────────────────────────────────────────┘ 27 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/testControlR-DisplayMultiline-zsh: -------------------------------------------------------------------------------- 1 | Search Query: > Slah 2 | 3 | ┌───────────────────────────────────────────────────────────────────────────────┐ 4 | │ Hostname Exit Code Command foo │ 5 | │───────────────────────────────────────────────────────────────────────────────│ 6 | │ ghaction-runner-hostname 0 "ls \\\n-Slah \\\n/" foo │ 7 | │ │ 8 | │ │ 9 | │ │ 10 | │ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | └───────────────────────────────────────────────────────────────────────────────┘ 27 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/testControlR-Final: -------------------------------------------------------------------------------- 1 | Search Query: > -pipefail -exit_code:0 2 | 3 | ┌─────────────────────────────────────────────────────────────────────────────┐ 4 | │ Hostname Exit Code Command foo │ 5 | │─────────────────────────────────────────────────────────────────────────────│ 6 | │ localhost 2 echo 'bar' & │ 7 | │ localhost 2 echo 'aaaaaa bbbb' │ 8 | │ localhost 2 ls ~/bar/ │ 9 | │ localhost 2 ls ~/foo/ │ 10 | │ server 127 ls ~/ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | └─────────────────────────────────────────────────────────────────────────────┘ 27 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/testControlR-Initial: -------------------------------------------------------------------------------- 1 | Search Query: > ls 2 | 3 | ┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 4 | │ Hostname CWD Timestamp Runtime Exit Code Command │ 5 | │────────────────────────────────────────────────────────────────────────────────────────────────────────│ 6 | │ localhost /tmp/ Oct 17 2022 21:43:36 PDT 3s 2 echo 'bar' & │ 7 | │ localhost /tmp/ Oct 17 2022 21:43:31 PDT 3s 2 echo 'aaaaaa bbbb' │ 8 | │ localhost /tmp/ Oct 17 2022 21:43:26 PDT 3s 2 ls ~/bar/ │ 9 | │ localhost /tmp/ Oct 17 2022 21:43:21 PDT 3s 2 ls ~/foo/ │ 10 | │ server /etc/ Oct 17 2022 21:43:16 PDT 3s 127 ls ~/ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | └────────────────────────────────────────────────────────────────────────────────────────────────────────┘ 27 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/testControlR-InitialExport: -------------------------------------------------------------------------------- 1 | ls ~/ 2 | ls ~/foo/ 3 | ls ~/bar/ 4 | echo 'aaaaaa bbbb' 5 | echo 'bar' & 6 | -------------------------------------------------------------------------------- /client/testdata/testControlR-InitialSearch: -------------------------------------------------------------------------------- 1 | Search Query: > ls 2 | 3 | ┌─────────────────────────────────────────────────────────────────────────────┐ 4 | │ Hostname Exit Code Command foo │ 5 | │─────────────────────────────────────────────────────────────────────────────│ 6 | │ ghaction-runner-hostname 0 ls / foo │ 7 | │ localhost 2 ls ~/bar/ │ 8 | │ localhost 2 ls ~/foo/ │ 9 | │ server 127 ls ~/ │ 10 | │ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | └─────────────────────────────────────────────────────────────────────────────┘ 27 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/testControlR-InitialSearchExpanded: -------------------------------------------------------------------------------- 1 | Search Query: > echo 2 | 3 | ┌─────────────────────────────────────────────────────────────────────────────┐ 4 | │ Hostname Exit Code Command foo │ 5 | │─────────────────────────────────────────────────────────────────────────────│ 6 | │ localhost 2 echo 'bar' & │ 7 | │ localhost 2 echo 'aaaaaa bbbb' │ 8 | │ │ 9 | │ │ 10 | │ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | └─────────────────────────────────────────────────────────────────────────────┘ 27 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/testControlR-InitialSearchNoResults: -------------------------------------------------------------------------------- 1 | Search Query: > asdf 2 | 3 | ┌─────────────────────────────────────────────────────────────────────────────┐ 4 | │ Hostname Exit Code Command foo │ 5 | │─────────────────────────────────────────────────────────────────────────────│ 6 | │ │ 7 | │ │ 8 | │ │ 9 | │ │ 10 | │ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | └─────────────────────────────────────────────────────────────────────────────┘ 27 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/testControlR-InitialSearchNoResultsThenFoundResults: -------------------------------------------------------------------------------- 1 | Search Query: > echo 2 | 3 | ┌─────────────────────────────────────────────────────────────────────────────┐ 4 | │ Hostname Exit Code Command foo │ 5 | │─────────────────────────────────────────────────────────────────────────────│ 6 | │ localhost 2 echo 'bar' & │ 7 | │ localhost 2 echo 'aaaaaa bbbb' │ 8 | │ │ 9 | │ │ 10 | │ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | └─────────────────────────────────────────────────────────────────────────────┘ 27 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/testControlR-Search: -------------------------------------------------------------------------------- 1 | Search Query: > echo 2 | 3 | ┌────────────────────────────────────────────────────────────────────────────────────────────────────────┐ 4 | │ Hostname CWD Timestamp Runtime Exit Code Command │ 5 | │────────────────────────────────────────────────────────────────────────────────────────────────────────│ 6 | │ localhost /tmp/ Oct 17 2022 21:43:36 PDT 3s 2 echo 'bar' & │ 7 | │ localhost /tmp/ Oct 17 2022 21:43:31 PDT 3s 2 echo 'aaaaaa bbbb' │ 8 | │ │ 9 | │ │ 10 | │ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | └────────────────────────────────────────────────────────────────────────────────────────────────────────┘ 27 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/testControlR-SelectMultiline-bash-darwin: -------------------------------------------------------------------------------- 1 | bash-5.2$ source /Users/runner/.bashrc 2 | bash-5.2$ ls -Slah / -------------------------------------------------------------------------------- /client/testdata/testControlR-SelectMultiline-bash-linux: -------------------------------------------------------------------------------- 1 | runner@ghaction-runner-hostname:~/work/hishtory/hishtory$ source /home/runner/.bashrc 2 | runner@ghaction-runner-hostname:~/work/hishtory/hishtory$ ls -Slah / -------------------------------------------------------------------------------- /client/testdata/testControlR-SelectMultiline-zsh-darwin: -------------------------------------------------------------------------------- 1 | runner@ghaction-runner-hostname hishtory % ls \ 2 | -Slah \ 3 | / -------------------------------------------------------------------------------- /client/testdata/testControlR-SelectMultiline-zsh-linux: -------------------------------------------------------------------------------- 1 | ghaction-runner-hostname% ls \ 2 | -Slah \ 3 | / -------------------------------------------------------------------------------- /client/testdata/testControlR-bash-Disabled-darwin: -------------------------------------------------------------------------------- 1 | bash-5.2$ source /Users/runner/.bashrc 2 | (reverse-i-search)`': -------------------------------------------------------------------------------- /client/testdata/testControlR-bash-Disabled-linux: -------------------------------------------------------------------------------- 1 | runner@ghaction-runner-hostname:~/work/hishtory/hishtory$ source /home/runner/.bashrc 2 | (reverse-i-search)`': -------------------------------------------------------------------------------- /client/testdata/testControlR-customColumn: -------------------------------------------------------------------------------- 1 | Search Query: > -pipefail 2 | 3 | ┌─────────────────────────────────────────────────────────────────────────────┐ 4 | │ Hostname Exit Code Command foo │ 5 | │─────────────────────────────────────────────────────────────────────────────│ 6 | │ ghaction-runner-hostname 0 ls / foo │ 7 | │ localhost 2 echo 'bar' & │ 8 | │ localhost 2 echo 'aaaaaa bbbb' │ 9 | │ localhost 2 ls ~/bar/ │ 10 | │ localhost 2 ls ~/foo/ │ 11 | │ server 127 ls ~/ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | └─────────────────────────────────────────────────────────────────────────────┘ 27 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/testControlR-displayedColumns: -------------------------------------------------------------------------------- 1 | Search Query: > ls 2 | 3 | ┌────────────────────────────────────────────────────┐ 4 | │ Hostname Exit Code Command │ 5 | │────────────────────────────────────────────────────│ 6 | │ localhost 2 echo 'bar' & │ 7 | │ localhost 2 echo 'aaaaaa bbbb' │ 8 | │ localhost 2 ls ~/bar/ │ 9 | │ localhost 2 ls ~/foo/ │ 10 | │ server 127 ls ~/ │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | └────────────────────────────────────────────────────┘ 27 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/testControlR-zsh-Disabled-darwin: -------------------------------------------------------------------------------- 1 | runner@ghaction-runner-hostname hishtory % 2 | bck-i-search: _ -------------------------------------------------------------------------------- /client/testdata/testControlR-zsh-Disabled-linux: -------------------------------------------------------------------------------- 1 | ghaction-runner-hostname% 2 | bck-i-search: _ -------------------------------------------------------------------------------- /client/testdata/testCustomColumns-initHistory: -------------------------------------------------------------------------------- 1 | export FOOBAR='hello' 2 | echo $FOOBAR world 3 | cd / 4 | echo baz 5 | -------------------------------------------------------------------------------- /client/testdata/testCustomColumns-query-isAction=false: -------------------------------------------------------------------------------- 1 | Exit Code git_remote Command 2 | 0 git@github.com:ddworken/hishtory.git hishtory config-set displayed-columns 'Exit Code' git_remote Command 3 | 0 echo bar 4 | 0 cd / 5 | 0 git@github.com:ddworken/hishtory.git echo foo 6 | 0 git@github.com:ddworken/hishtory.git hishtory config-add custom-columns git_remote '(git remote -v 2>/dev/null | grep origin 1>/dev/null ) && git remote get-url origin || true' 7 | 0 echo baz 8 | 0 cd / 9 | 0 echo $FOOBAR world 10 | 0 export FOOBAR='hello' 11 | -------------------------------------------------------------------------------- /client/testdata/testCustomColumns-query-isAction=true: -------------------------------------------------------------------------------- 1 | Exit Code git_remote Command 2 | 0 https://github.com/ddworken/hishtory hishtory config-set displayed-columns 'Exit Code' git_remote Command 3 | 0 echo bar 4 | 0 cd / 5 | 0 https://github.com/ddworken/hishtory echo foo 6 | 0 https://github.com/ddworken/hishtory hishtory config-add custom-columns git_remote '(git remote -v 2>/dev/null | grep origin 1>/dev/null ) && git remote get-url origin || true' 7 | 0 echo baz 8 | 0 cd / 9 | 0 echo $FOOBAR world 10 | 0 export FOOBAR='hello' 11 | -------------------------------------------------------------------------------- /client/testdata/testDisplayTable-customColumns: -------------------------------------------------------------------------------- 1 | Hostname Command 2 | localhost table_cmd2 3 | localhost table_cmd1 4 | -------------------------------------------------------------------------------- /client/testdata/testDisplayTable-customColumns-2: -------------------------------------------------------------------------------- 1 | Hostname Exit Code Command 2 | localhost 3 table_cmd2 3 | localhost 2 table_cmd1 4 | -------------------------------------------------------------------------------- /client/testdata/testDisplayTable-customColumns-3: -------------------------------------------------------------------------------- 1 | Hostname Exit Code Command CWD 2 | localhost 3 table_cmd2 ~/foo/ 3 | localhost 2 table_cmd1 /tmp/ 4 | -------------------------------------------------------------------------------- /client/testdata/testDisplayTable-customColumns-multiLineCommand: -------------------------------------------------------------------------------- 1 | Hostname Exit Code Command CWD 2 | localhost 2 while : /tmp/ 3 | do 4 | ls /table/ 5 | done 6 | localhost 3 table_cmd2 ~/foo/ 7 | localhost 2 table_cmd1 /tmp/ 8 | -------------------------------------------------------------------------------- /client/testdata/testDisplayTable-customColumns-trulyCustom: -------------------------------------------------------------------------------- 1 | Hostname Exit Code Command CWD foo 2 | ghaction-runner-hostname 0 echo table-2 / aaaaaaaaaaaaa 3 | ghaction-runner-hostname 0 echo table-1 / aaaaaaaaaaaaa 4 | localhost 2 while : /tmp/ 5 | do 6 | ls /table/ 7 | done 8 | localhost 3 table_cmd2 ~/foo/ 9 | localhost 2 table_cmd1 /tmp/ 10 | -------------------------------------------------------------------------------- /client/testdata/testDisplayTable-defaultColumns: -------------------------------------------------------------------------------- 1 | Hostname CWD Timestamp Runtime Exit Code Command 2 | localhost ~/foo/ Apr 16 2022 01:03:16 PDT 24s 3 table_cmd2 3 | localhost /tmp/ Apr 16 2022 01:03:06 PDT 4s 2 table_cmd1 4 | -------------------------------------------------------------------------------- /client/testdata/testIntegrationWithNewDevice-bash: -------------------------------------------------------------------------------- 1 | hishtory status 2 | hishtory query 3 | ls /a 4 | ls /bar 5 | ls /foo 6 | echo foo 7 | echo bar 8 | hishtory disable 9 | hishtory enable 10 | echo thisisrecorded 11 | hishtory query 12 | hishtory query foo 13 | echo hello | grep complex | sed s/h/i/g; echo baz && echo "fo 'o" # mycommand 14 | hishtory query complex 15 | hishtory query 16 | echo mynewcommand 17 | hishtory query 18 | hishtory query 19 | echo mynewercommand 20 | hishtory query 21 | othercomputer 22 | hishtory query 23 | hishtory reupload 24 | -------------------------------------------------------------------------------- /client/testdata/testIntegrationWithNewDevice-tablebash: -------------------------------------------------------------------------------- 1 | Hostname Exit Code Command 2 | ghaction-runner-hostname 0 hishtory config-set displayed-columns Hostname 'Exit Code' Command 3 | ghaction-runner-hostname 0 hishtory reupload 4 | ghaction-runner-hostname 0 hishtory query 5 | localhost 2 othercomputer 6 | ghaction-runner-hostname 0 hishtory query 7 | ghaction-runner-hostname 0 echo mynewercommand 8 | ghaction-runner-hostname 0 hishtory query 9 | ghaction-runner-hostname 0 hishtory query 10 | ghaction-runner-hostname 0 echo mynewcommand 11 | ghaction-runner-hostname 0 hishtory query 12 | ghaction-runner-hostname 0 hishtory query complex 13 | ghaction-runner-hostname 0 echo hello | grep complex | sed s/h/i/g; echo baz && echo "fo 'o" # mycommand 14 | ghaction-runner-hostname 0 hishtory query foo 15 | ghaction-runner-hostname 0 hishtory query 16 | ghaction-runner-hostname 0 echo thisisrecorded 17 | ghaction-runner-hostname 0 hishtory enable 18 | ghaction-runner-hostname 0 hishtory disable 19 | ghaction-runner-hostname 0 echo bar 20 | ghaction-runner-hostname 0 echo foo 21 | ghaction-runner-hostname 0 hishtory query 22 | ghaction-runner-hostname 0 hishtory status 23 | -------------------------------------------------------------------------------- /client/testdata/testIntegrationWithNewDevice-tablezsh: -------------------------------------------------------------------------------- 1 | Hostname Exit Code Command 2 | ghaction-runner-hostname 0 hishtory config-set displayed-columns Hostname 'Exit Code' Command 3 | ghaction-runner-hostname 0 hishtory reupload 4 | ghaction-runner-hostname 0 hishtory query 5 | localhost 2 othercomputer 6 | ghaction-runner-hostname 0 hishtory query 7 | ghaction-runner-hostname 0 echo mynewercommand 8 | ghaction-runner-hostname 0 hishtory query 9 | ghaction-runner-hostname 0 hishtory query 10 | ghaction-runner-hostname 0 echo mynewcommand 11 | ghaction-runner-hostname 0 hishtory query 12 | ghaction-runner-hostname 0 hishtory query complex 13 | ghaction-runner-hostname 0 echo hello | grep complex | sed s/h/i/g; echo baz && echo "fo 'o" # mycommand 14 | ghaction-runner-hostname 0 hishtory query foo 15 | ghaction-runner-hostname 0 hishtory query 16 | ghaction-runner-hostname 0 echo thisisrecorded 17 | ghaction-runner-hostname 0 hishtory enable 18 | ghaction-runner-hostname 0 hishtory disable 19 | ghaction-runner-hostname 0 echo bar 20 | ghaction-runner-hostname 0 echo foo 21 | ghaction-runner-hostname 0 hishtory query 22 | ghaction-runner-hostname 0 hishtory status 23 | -------------------------------------------------------------------------------- /client/testdata/testIntegrationWithNewDevice-zsh: -------------------------------------------------------------------------------- 1 | hishtory status 2 | hishtory query 3 | ls /a 4 | ls /bar 5 | ls /foo 6 | echo foo 7 | echo bar 8 | hishtory disable 9 | hishtory enable 10 | echo thisisrecorded 11 | hishtory query 12 | hishtory query foo 13 | echo hello | grep complex | sed s/h/i/g; echo baz && echo "fo 'o" # mycommand 14 | hishtory query complex 15 | hishtory query 16 | echo mynewcommand 17 | hishtory query 18 | hishtory query 19 | echo mynewercommand 20 | hishtory query 21 | othercomputer 22 | hishtory query 23 | hishtory reupload 24 | -------------------------------------------------------------------------------- /client/testdata/testPresaving-query: -------------------------------------------------------------------------------- 1 | CWD Runtime Command 2 | / N/A sleep 13371337 3 | -------------------------------------------------------------------------------- /client/testdata/testPresavingOffline-query-missing: -------------------------------------------------------------------------------- 1 | CWD Runtime Command 2 | -------------------------------------------------------------------------------- /client/testdata/testPresavingOffline-query-present: -------------------------------------------------------------------------------- 1 | CWD Runtime Command 2 | / N/A sleep 13371336 3 | -------------------------------------------------------------------------------- /client/testdata/testRemoveDuplicateRows-enabled-export: -------------------------------------------------------------------------------- 1 | echo foo 2 | echo foo 3 | echo baz 4 | echo baz 5 | echo foo 6 | hishtory config-set displayed-columns 'Exit Code' Command 7 | hishtory tquery 8 | hishtory config-set filter-duplicate-commands true 9 | -------------------------------------------------------------------------------- /client/testdata/testRemoveDuplicateRows-enabled-query: -------------------------------------------------------------------------------- 1 | Exit Code Command 2 | 0 hishtory config-set filter-duplicate-commands true 3 | 0 hishtory tquery 4 | 0 hishtory config-set displayed-columns 'Exit Code' Command 5 | 0 echo foo 6 | 0 echo baz 7 | -------------------------------------------------------------------------------- /client/testdata/testRemoveDuplicateRows-enabled-tquery: -------------------------------------------------------------------------------- 1 | Search Query: > -pipefail 2 | 3 | ┌───────────────────────────────────────────────────────────────────────────┐ 4 | │ Exit Code Command │ 5 | │───────────────────────────────────────────────────────────────────────────│ 6 | │ 0 hishtory tquery │ 7 | │ 0 hishtory config-set filter-duplicate-commands true │ 8 | │ 0 hishtory config-set displayed-columns 'Exit Code' Command │ 9 | │ 0 echo foo │ 10 | │ 0 echo baz │ 11 | │ │ 12 | │ │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | └───────────────────────────────────────────────────────────────────────────┘ 27 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/testRemoveDuplicateRows-export: -------------------------------------------------------------------------------- 1 | echo foo 2 | echo foo 3 | echo baz 4 | echo baz 5 | echo foo 6 | -------------------------------------------------------------------------------- /client/testdata/testRemoveDuplicateRows-query: -------------------------------------------------------------------------------- 1 | Exit Code Command 2 | 0 hishtory config-set displayed-columns 'Exit Code' Command 3 | 0 echo foo 4 | 0 echo baz 5 | 0 echo baz 6 | 0 echo foo 7 | 0 echo foo 8 | -------------------------------------------------------------------------------- /client/testdata/testRemoveDuplicateRows-tquery: -------------------------------------------------------------------------------- 1 | Search Query: > -pipefail 2 | 3 | ┌───────────────────────────────────────────────────────────────────────────┐ 4 | │ Exit Code Command │ 5 | │───────────────────────────────────────────────────────────────────────────│ 6 | │ 0 hishtory tquery │ 7 | │ 0 hishtory config-set displayed-columns 'Exit Code' Command │ 8 | │ 0 echo foo │ 9 | │ 0 echo baz │ 10 | │ 0 echo baz │ 11 | │ 0 echo foo │ 12 | │ 0 echo foo │ 13 | │ │ 14 | │ │ 15 | │ │ 16 | │ │ 17 | │ │ 18 | │ │ 19 | │ │ 20 | │ │ 21 | │ │ 22 | │ │ 23 | │ │ 24 | │ │ 25 | │ │ 26 | └───────────────────────────────────────────────────────────────────────────┘ 27 | hiSHtory: Search your shell history • ctrl+h help -------------------------------------------------------------------------------- /client/testdata/testTabCompletion-suggestions-fish: -------------------------------------------------------------------------------- 1 | config-add (Add a config option) config-delete (Delete a config option) config-get (Get the value of a config option) config-set (Set the value of a config option) -------------------------------------------------------------------------------- /client/testdata/testTabCompletion-suggestions-zsh: -------------------------------------------------------------------------------- 1 | config-add -- Add a config option 2 | config-delete -- Delete a config option 3 | config-get -- Get the value of a config option 4 | config-set -- Set the value of a config option -------------------------------------------------------------------------------- /client/testdata/testUninstall-post-uninstall: -------------------------------------------------------------------------------- 1 | foo 2 | bar 3 | -------------------------------------------------------------------------------- /client/testdata/testUninstall-post-uninstall-bash-darwin: -------------------------------------------------------------------------------- 1 | bash-5.2$ source /Users/runner/.bashrc 2 | bash-5.2$ echo foo 3 | foo 4 | bash-5.2$ hishtory 5 | bash: hishtory: command not found 6 | bash-5.2$ echo bar 7 | bar 8 | bash-5.2$ -------------------------------------------------------------------------------- /client/testdata/testUninstall-post-uninstall-bash-linux: -------------------------------------------------------------------------------- 1 | runner@ghaction-runner-hostname:~/work/hishtory/hishtory$ source /home/runner/.bashrc 2 | runner@ghaction-runner-hostname:~/work/hishtory/hishtory$ echo foo 3 | foo 4 | runner@ghaction-runner-hostname:~/work/hishtory/hishtory$ hishtory 5 | hishtory: command not found 6 | runner@ghaction-runner-hostname:~/work/hishtory/hishtory$ echo bar 7 | bar 8 | runner@ghaction-runner-hostname:~/work/hishtory/hishtory$ -------------------------------------------------------------------------------- /client/testdata/testUninstall-post-uninstall-zsh-darwin: -------------------------------------------------------------------------------- 1 | runner@ghaction-runner-hostname hishtory % echo foo 2 | foo 3 | runner@ghaction-runner-hostname hishtory % hishtory 4 | zsh: command not found: hishtory 5 | runner@ghaction-runner-hostname hishtory % echo bar 6 | bar 7 | runner@ghaction-runner-hostname hishtory % -------------------------------------------------------------------------------- /client/testdata/testUninstall-post-uninstall-zsh-linux: -------------------------------------------------------------------------------- 1 | ghaction-runner-hostname% echo foo 2 | foo 3 | ghaction-runner-hostname% hishtory 4 | zsh: command not found: hishtory 5 | ghaction-runner-hostname% echo bar 6 | bar 7 | ghaction-runner-hostname% -------------------------------------------------------------------------------- /client/testdata/testUninstall-recorded: -------------------------------------------------------------------------------- 1 | echo foo 2 | echo baz 3 | -------------------------------------------------------------------------------- /client/testdata/testUninstall-uninstall-bash: -------------------------------------------------------------------------------- 1 | Are you sure you want to uninstall hiSHtory and delete all locally saved history data [y/N]Do you have any feedback on why you're uninstallying hiSHtory? Type any feedback and then hit enter. 2 | Feedback: Successfully uninstalled hishtory, please restart your terminal... 3 | bash: /home/runner/.hishtory/hishtory: No such file or directory 4 | bash: /home/runner/.hishtory/hishtory: No such file or directory 5 | -------------------------------------------------------------------------------- /client/testdata/testUninstall-uninstall-zsh: -------------------------------------------------------------------------------- 1 | Are you sure you want to uninstall hiSHtory and delete all locally saved history data [y/N]Do you have any feedback on why you're uninstallying hiSHtory? Type any feedback and then hit enter. 2 | Feedback: Successfully uninstalled hishtory, please restart your terminal... 3 | _hishtory_precmd:8: command not found: hishtory 4 | _hishtory_precmd:9: command not found: hishtory 5 | -------------------------------------------------------------------------------- /client/testdata/unittestTable-truncatedTable: -------------------------------------------------------------------------------- 1 | Column1 Column2 2 | a1 a2345 3 | b1 b23 4 | c1 c1234567890abcdefgh… 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /client/testdata/unittestTable-truncatedTable-right1: -------------------------------------------------------------------------------- 1 | Column1 Column2 2 | a1 …2345 3 | b1 …23 4 | c1 …1234567890abcdefgh… 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /client/testdata/unittestTable-truncatedTable-right2: -------------------------------------------------------------------------------- 1 | Column1 Column2 2 | a1 …345 3 | b1 …3 4 | c1 …234567890abcdefghi… 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /client/testdata/unittestTable-truncatedTable-right3: -------------------------------------------------------------------------------- 1 | Column1 Column2 2 | a1 …45 3 | b1 … 4 | c1 …34567890abcdefghij… 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /client/tui/tui_test.go: -------------------------------------------------------------------------------- 1 | package tui 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestCalculateWordBoundaries(t *testing.T) { 10 | require.Equal(t, []int{0, 3}, calculateWordBoundaries("foo")) 11 | require.Equal(t, []int{0, 3, 7}, calculateWordBoundaries("foo bar")) 12 | require.Equal(t, []int{0, 3, 7}, calculateWordBoundaries("foo-bar")) 13 | require.Equal(t, []int{0, 3, 7, 11}, calculateWordBoundaries("foo-bar baz")) 14 | require.Equal(t, []int{0, 3, 10, 16}, calculateWordBoundaries("foo-- -bar - baz")) 15 | require.Equal(t, []int{0, 3}, calculateWordBoundaries("foo ")) 16 | } 17 | 18 | func TestSanitizeEscapeCodes(t *testing.T) { 19 | require.Equal(t, "foo", sanitizeEscapeCodes("foo")) 20 | require.Equal(t, "foo\x1b[31mbar", sanitizeEscapeCodes("foo\x1b[31mbar")) 21 | require.Equal(t, "", sanitizeEscapeCodes("11;rgb:1c1c/1c1c/1c1c")) 22 | require.Equal(t, "foo bar", sanitizeEscapeCodes("foo 11;rgb:1c1c/1c1c/1c1c bar")) 23 | } 24 | -------------------------------------------------------------------------------- /client/webui/templates/webui.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

hiSHtory

5 |

Your shell history in context, synced, and queryable

6 |
7 |
8 |
9 | 10 | 34 | 35 |
36 | 37 | {{ block "resultsTable.html" . }} 38 |
39 | 40 | 41 | 42 | {{ range .ColumnNames }} 43 | 44 | {{ end }} 45 | 46 | 47 | 48 | {{ range .SearchResults }} 49 | 50 | {{ range . }} 51 | 52 | {{ end }} 53 | 54 | {{ end }} 55 | 56 |
{{ . }}
{{ . }}
57 |
58 | {{ end }} 59 | 60 | 61 | 67 | 72 | 77 | -------------------------------------------------------------------------------- /demo.vhs: -------------------------------------------------------------------------------- 1 | # Demo file uses https://github.com/charmbracelet/vhs 2 | 3 | Output backend/web/landing/www/img/demo.gif 4 | Set FontSize 22 5 | Set Width 2300 6 | Set Height 1050 7 | 8 | # Set up 9 | Hide 10 | Type "zsh" 11 | Enter 12 | Type "setopt interactivecomments" 13 | Enter 14 | Type "clear" 15 | Enter 16 | Set TypingSpeed 0.1 17 | Show 18 | 19 | Type "find . -iname '*.go' | xargs -I {} -- gofmt -w {}" 20 | Enter 21 | Sleep 4000ms 22 | 23 | Type "ssh server" 24 | Enter 25 | Sleep 400ms 26 | Type "# Then press control + r to search your history" 27 | Enter 28 | Sleep 3800ms 29 | 30 | Ctrl+R 31 | Sleep 6000ms 32 | Type "g" 33 | Sleep 400ms 34 | Type "of 35 | Sleep 400ms 36 | Type "mt" 37 | Sleep 1000ms 38 | Type " cwd:~/code/hishtory/" 39 | Sleep 7000ms 40 | Enter 41 | Sleep 6000ms 42 | -------------------------------------------------------------------------------- /docs/offline-binary.md: -------------------------------------------------------------------------------- 1 | # Offline Binary 2 | 3 | hiSHtory supports disabling syncing at install-time via `curl https://hishtory.dev/install.py | python3 - --offline` or at config-time via `hishtory syncing disable`. This will disable persisting your (encrypted) history on the backend API server. For most users, this is the recommended option for running hiSHtory in an offline environment since it still supports opt-in updates via `hishtory update`. 4 | 5 | But, if you need stronger guarantees that hiSHtory will not make any network requests, this can also be done by compiling your own copy of hiSHtory with the `offline` tag. This will statically link in [`net_disabled.go`](https://github.com/ddworken/hishtory/blob/master/client/lib/net_disabled.go) which will guarantee that the binary cannot make any HTTP requests. To use this: 6 | 7 | ``` 8 | git clone https://github.com/ddworken/hishtory 9 | cd hishtory 10 | go build -tags offline 11 | ./hishtory install 12 | ``` 13 | 14 | This binary will be entirely offline and is guaranteed to never make any requests to `api.hishtory.dev`. 15 | -------------------------------------------------------------------------------- /hishtory.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/ddworken/hishtory/client/cmd" 5 | "github.com/ddworken/hishtory/client/data" 6 | "github.com/ddworken/hishtory/client/lib" 7 | ) 8 | 9 | func main() { 10 | lib.CheckFatalError(data.ValidateHishtoryPath()) 11 | cmd.Execute() 12 | } 13 | 14 | // TODO(feature): Add a session_id column that corresponds to the shell session the command was run in 15 | // TODO(feature): Add a shell column that contains the shell name 16 | -------------------------------------------------------------------------------- /scripts/actions-sign.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | import subprocess 4 | 5 | def main(): 6 | assertPresentAndNotAscii("hishtory-darwin-arm64") 7 | assertPresentAndNotAscii("hishtory-darwin-amd64") 8 | 9 | print("before sha1sum:") 10 | os.system("sha1sum hishtory-* 2>&1") 11 | print("before sha256sum:") 12 | os.system("sha256sum hishtory-* 2>&1") 13 | 14 | print("file:") 15 | os.system("file hishtory-* 2>&1") 16 | 17 | print("signing...") 18 | os.system(""" 19 | set -emo pipefail 20 | cp hishtory-darwin-arm64 hishtory-darwin-arm64-unsigned 21 | cp hishtory-darwin-amd64 hishtory-darwin-amd64-unsigned 22 | echo $MACOS_CERTIFICATE | base64 -d > certificate.p12 23 | security create-keychain -p $MACOS_CERTIFICATE_PWD build.keychain 24 | security default-keychain -s build.keychain 25 | security unlock-keychain -p $MACOS_CERTIFICATE_PWD build.keychain 26 | security import certificate.p12 -k build.keychain -P $MACOS_CERTIFICATE_PWD -T /usr/bin/codesign 27 | security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k $MACOS_CERTIFICATE_PWD build.keychain 28 | /usr/bin/codesign --force -s 6D4E1575A0D40C370E294916A8390797106C8A6E hishtory-darwin-arm64 -v 29 | /usr/bin/codesign --force -s 6D4E1575A0D40C370E294916A8390797106C8A6E hishtory-darwin-amd64 -v 30 | """) 31 | 32 | print("after sha1sum:") 33 | os.system("sha1sum hishtory-* 2>&1") 34 | print("after sha256sum:") 35 | os.system("sha256sum hishtory-* 2>&1") 36 | 37 | 38 | def assertPresentAndNotAscii(fn): 39 | if not os.path.exists(fn): 40 | raise Exception(f"{fn=} does not exist, did it fail to download?") 41 | out = subprocess.check_output(["file", fn]).decode('utf-8') 42 | if "ASCII text" in out: 43 | raise Exception(f"{fn=} is of type {out}") 44 | 45 | if __name__ == '__main__': 46 | main() -------------------------------------------------------------------------------- /scripts/client-ldflags: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | GIT_HASH=$(git rev-parse HEAD) 4 | echo "-X github.com/ddworken/hishtory/client/lib.GitCommit=$GIT_HASH -X github.com/ddworken/hishtory/client/lib.Version=`cat VERSION` -w -extldflags \"-static\"" 5 | -------------------------------------------------------------------------------- /shared/ai/ai_test.go: -------------------------------------------------------------------------------- 1 | package ai 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/ddworken/hishtory/shared/testutils" 9 | 10 | "github.com/stretchr/testify/require" 11 | ) 12 | 13 | // A basic sanity test that our integration with the OpenAI API is correct and is returning reasonable results (at least for a very basic query) 14 | func TestLiveOpenAiApi(t *testing.T) { 15 | if os.Getenv("OPENAI_API_KEY") == "" { 16 | if testutils.IsGithubAction() && testutils.GetCurrentGitBranch(t) == testutils.DefaultGitBranchName { 17 | t.Fatal("OPENAI_API_KEY is not set, cannot run TestLiveOpenAiApi") 18 | } else { 19 | t.Skip("Skipping test since OPENAI_API_KEY is not set") 20 | } 21 | } 22 | results, _, err := GetAiSuggestionsViaOpenAiApi("https://api.openai.com/v1/chat/completions", "list files in the current directory", "bash", "Linux", "", 3) 23 | require.NoError(t, err) 24 | resultsContainsLs := false 25 | for _, result := range results { 26 | if strings.Contains(result, "ls") { 27 | resultsContainsLs = true 28 | } 29 | } 30 | require.Truef(t, resultsContainsLs, "expected results=%#v to contain ls", results) 31 | } 32 | -------------------------------------------------------------------------------- /shared/time.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | const DateOnly = "2006-01-02" 4 | -------------------------------------------------------------------------------- /shared/utils.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | import "sync" 4 | 5 | func ForEach[T any](arr []T, numThreads int, fn func(T) error) error { 6 | wg := &sync.WaitGroup{} 7 | wg.Add(len(arr)) 8 | 9 | limiter := make(chan bool, numThreads) 10 | 11 | var errors []error 12 | for _, item := range arr { 13 | limiter <- true 14 | go func(x T) { 15 | defer wg.Done() 16 | err := fn(x) 17 | if err != nil { 18 | errors = append(errors, err) 19 | } 20 | <-limiter 21 | }(item) 22 | if len(errors) > 0 { 23 | return errors[0] 24 | } 25 | } 26 | 27 | wg.Wait() 28 | if len(errors) > 0 { 29 | return errors[0] 30 | } 31 | return nil 32 | } 33 | -------------------------------------------------------------------------------- /shared/version.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | import ( 4 | "fmt" 5 | "regexp" 6 | "strconv" 7 | ) 8 | 9 | type ParsedVersion struct { 10 | MajorVersion int 11 | MinorVersion int 12 | } 13 | 14 | func (pv ParsedVersion) GreaterThan(other ParsedVersion) bool { 15 | if pv.MajorVersion == other.MajorVersion && pv.MinorVersion == other.MinorVersion { 16 | return false 17 | } 18 | return !pv.LessThan(other) 19 | } 20 | 21 | func (pv ParsedVersion) LessThan(other ParsedVersion) bool { 22 | if pv.MajorVersion != other.MajorVersion { 23 | return pv.MajorVersion < other.MajorVersion 24 | } 25 | return pv.MinorVersion < other.MinorVersion 26 | } 27 | 28 | func (pv ParsedVersion) Decrement() ParsedVersion { 29 | if pv.MinorVersion > 1 { 30 | return ParsedVersion{pv.MajorVersion, pv.MinorVersion - 1} 31 | } 32 | panic("cannot decrement() when MinorVersion == 0") 33 | } 34 | 35 | func (pv ParsedVersion) String() string { 36 | return fmt.Sprintf("v%d.%d", pv.MajorVersion, pv.MinorVersion) 37 | } 38 | 39 | func ParseVersionString(versionString string) (ParsedVersion, error) { 40 | re := regexp.MustCompile(`v(\d+)[.](\d+)`) 41 | matches := re.FindAllStringSubmatch(versionString, -1) 42 | if len(matches) != 1 { 43 | return ParsedVersion{}, fmt.Errorf("failed to parse version=%#v (matches=%#v)", versionString, matches) 44 | } 45 | if len(matches[0]) != 3 { 46 | return ParsedVersion{}, fmt.Errorf("failed to parse version=%#v (matches[0]=%#v)", versionString, matches[0]) 47 | } 48 | MajorVersion, err := strconv.Atoi(matches[0][1]) 49 | if err != nil { 50 | return ParsedVersion{}, fmt.Errorf("failed to parse major version %#v", matches[0][1]) 51 | } 52 | MinorVersion, err := strconv.Atoi(matches[0][2]) 53 | if err != nil { 54 | return ParsedVersion{}, fmt.Errorf("failed to parse minor version %#v", matches[0][2]) 55 | } 56 | return ParsedVersion{MajorVersion, MinorVersion}, nil 57 | } 58 | -------------------------------------------------------------------------------- /shared/version_test.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestParseVersionString(t *testing.T) { 10 | p, err := ParseVersionString("v0.200") 11 | require.NoError(t, err) 12 | require.Equal(t, ParsedVersion{MajorVersion: 0, MinorVersion: 200}, p) 13 | p, err = ParseVersionString("v1.200") 14 | require.NoError(t, err) 15 | require.Equal(t, ParsedVersion{MajorVersion: 1, MinorVersion: 200}, p) 16 | p, err = ParseVersionString("v1.0") 17 | require.NoError(t, err) 18 | require.Equal(t, ParsedVersion{MajorVersion: 1, MinorVersion: 0}, p) 19 | p, err = ParseVersionString("v0.216") 20 | require.NoError(t, err) 21 | require.Equal(t, ParsedVersion{MajorVersion: 0, MinorVersion: 216}, p) 22 | p, err = ParseVersionString("v123.456") 23 | require.NoError(t, err) 24 | require.Equal(t, ParsedVersion{MajorVersion: 123, MinorVersion: 456}, p) 25 | } 26 | 27 | func TestVersionLessThan(t *testing.T) { 28 | require.False(t, ParsedVersion{0, 200}.LessThan(ParsedVersion{0, 200})) 29 | require.False(t, ParsedVersion{1, 200}.LessThan(ParsedVersion{1, 200})) 30 | require.False(t, ParsedVersion{0, 201}.LessThan(ParsedVersion{0, 200})) 31 | require.False(t, ParsedVersion{1, 0}.LessThan(ParsedVersion{0, 200})) 32 | require.True(t, ParsedVersion{0, 199}.LessThan(ParsedVersion{0, 200})) 33 | require.True(t, ParsedVersion{0, 200}.LessThan(ParsedVersion{0, 205})) 34 | require.True(t, ParsedVersion{1, 200}.LessThan(ParsedVersion{1, 205})) 35 | require.True(t, ParsedVersion{0, 200}.LessThan(ParsedVersion{1, 1})) 36 | } 37 | 38 | func TestVersionGreaterThan(t *testing.T) { 39 | require.False(t, ParsedVersion{0, 200}.GreaterThan(ParsedVersion{0, 200})) 40 | require.False(t, ParsedVersion{1, 200}.GreaterThan(ParsedVersion{1, 200})) 41 | require.True(t, ParsedVersion{0, 201}.GreaterThan(ParsedVersion{0, 200})) 42 | require.True(t, ParsedVersion{1, 0}.GreaterThan(ParsedVersion{0, 200})) 43 | require.True(t, ParsedVersion{1, 1}.GreaterThan(ParsedVersion{1, 0})) 44 | require.False(t, ParsedVersion{0, 199}.GreaterThan(ParsedVersion{0, 200})) 45 | require.False(t, ParsedVersion{0, 200}.GreaterThan(ParsedVersion{0, 205})) 46 | require.False(t, ParsedVersion{1, 200}.GreaterThan(ParsedVersion{1, 205})) 47 | require.False(t, ParsedVersion{0, 200}.GreaterThan(ParsedVersion{1, 1})) 48 | } 49 | --------------------------------------------------------------------------------