diff --git a/backend/server/server.go b/backend/server/server.go index 19050cb..5f0e0ed 100644 --- a/backend/server/server.go +++ b/backend/server/server.go @@ -196,7 +196,7 @@ func apiSubmitHandler(ctx context.Context, w http.ResponseWriter, r *http.Reques return nil }) if err != nil { - panic(fmt.Errorf("failed to execute transaction to add entries to DB: %v", err)) + panic(fmt.Errorf("failed to execute transaction to add entries to DB: %w", err)) } if GLOBAL_STATSD != nil { GLOBAL_STATSD.Count("hishtory.submit", int64(len(devices)), []string{}, 1.0) @@ -312,7 +312,7 @@ func apiGetPendingDumpRequestsHandler(ctx context.Context, w http.ResponseWriter checkGormResult(GLOBAL_DB.WithContext(ctx).Where("user_id = ? AND requesting_device_id != ?", userId, deviceId).Find(&dumpRequests)) respBody, err := json.Marshal(dumpRequests) if err != nil { - panic(fmt.Errorf("failed to JSON marshall the dump requests: %v", err)) + panic(fmt.Errorf("failed to JSON marshall the dump requests: %w", err)) } w.Write(respBody) } @@ -342,7 +342,7 @@ func apiSubmitDumpHandler(ctx context.Context, w http.ResponseWriter, r *http.Re return nil }) if err != nil { - panic(fmt.Errorf("failed to execute transaction to add dumped DB: %v", err)) + panic(fmt.Errorf("failed to execute transaction to add dumped DB: %w", err)) } checkGormResult(GLOBAL_DB.WithContext(ctx).Delete(&shared.DumpRequest{}, "user_id = ? AND requesting_device_id = ?", userId, requestingDeviceId)) updateUsageData(ctx, r, userId, srcDeviceId, len(entries), false) @@ -372,7 +372,7 @@ func getDeletionRequestsHandler(ctx context.Context, w http.ResponseWriter, r *h checkGormResult(GLOBAL_DB.WithContext(ctx).Where("user_id = ? AND destination_device_id = ?", userId, deviceId).Find(&deletionRequests)) respBody, err := json.Marshal(deletionRequests) if err != nil { - panic(fmt.Errorf("failed to JSON marshall the dump requests: %v", err)) + panic(fmt.Errorf("failed to JSON marshall the dump requests: %w", err)) } w.Write(respBody) } @@ -491,11 +491,11 @@ func OpenDB() (*gorm.DB, error) { if isTestEnvironment() { db, err := gorm.Open(sqlite.Open("file::memory:?_journal_mode=WAL&cache=shared"), &gorm.Config{}) if err != nil { - return nil, fmt.Errorf("failed to connect to the DB: %v", err) + return nil, fmt.Errorf("failed to connect to the DB: %w", err) } underlyingDb, err := db.DB() if err != nil { - return nil, fmt.Errorf("failed to access underlying DB: %v", err) + return nil, fmt.Errorf("failed to access underlying DB: %w", err) } underlyingDb.SetMaxOpenConns(1) db.Exec("PRAGMA journal_mode = WAL") @@ -521,7 +521,7 @@ func OpenDB() (*gorm.DB, error) { var err error db, err = gorm.Open(sqlite.Open(sqliteDb), &gorm.Config{Logger: customLogger}) if err != nil { - return nil, fmt.Errorf("failed to connect to the DB: %v", err) + return nil, fmt.Errorf("failed to connect to the DB: %w", err) } } else { postgresDb := fmt.Sprintf(PostgresDb, os.Getenv("POSTGRESQL_PASSWORD")) @@ -535,7 +535,7 @@ func OpenDB() (*gorm.DB, error) { } db, err = gormtrace.Open(postgres.New(postgres.Config{Conn: sqlDb}), &gorm.Config{Logger: customLogger}) if err != nil { - return nil, fmt.Errorf("failed to connect to the DB: %v", err) + return nil, fmt.Errorf("failed to connect to the DB: %w", err) } } AddDatabaseTables(db) @@ -602,12 +602,12 @@ type releaseInfo struct { func updateReleaseVersion() error { resp, err := http.Get("https://api.github.com/repos/ddworken/hishtory/releases/latest") if err != nil { - return fmt.Errorf("failed to get latest release version: %v", err) + return fmt.Errorf("failed to get latest release version: %w", err) } defer resp.Body.Close() respBody, err := io.ReadAll(resp.Body) if err != nil { - return fmt.Errorf("failed to read github API response body: %v", err) + return fmt.Errorf("failed to read github API response body: %w", err) } if resp.StatusCode == 403 && strings.Contains(string(respBody), "API rate limit exceeded for ") { return nil @@ -618,7 +618,7 @@ func updateReleaseVersion() error { var info releaseInfo err = json.Unmarshal(respBody, &info) if err != nil { - return fmt.Errorf("failed to parse github API response: %v", err) + return fmt.Errorf("failed to parse github API response: %w", err) } latestVersionTag := info.Name ReleaseVersion = decrementVersionIfInvalid(latestVersionTag) @@ -654,7 +654,7 @@ func assertValidUpdate(updateInfo shared.UpdateInfo) error { for _, url := range urls { resp, err := http.Get(url) if err != nil { - return fmt.Errorf("failed to retrieve URL %#v: %v", url, err) + return fmt.Errorf("failed to retrieve URL %#v: %w", url, err) } defer resp.Body.Close() if resp.StatusCode == 404 { @@ -878,7 +878,7 @@ func deepCleanDatabase(ctx context.Context) { return nil }) if err != nil { - panic(fmt.Errorf("failed to deep clean DB: %v", err)) + panic(fmt.Errorf("failed to deep clean DB: %w", err)) } } diff --git a/client/client_test.go b/client/client_test.go index 7079bbd..5ecc12e 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -75,7 +75,7 @@ func (b bashTester) RunInteractiveShellRelaxed(t testing.TB, script string) (str cmd.Stderr = &stderr err := cmd.Run() if err != nil { - return "", fmt.Errorf("unexpected error when running commands, out=%#v, err=%#v: %v", stdout.String(), stderr.String(), err) + return "", fmt.Errorf("unexpected error when running commands, out=%#v, err=%#v: %w", stdout.String(), stderr.String(), err) } outStr := stdout.String() require.NotContains(t, outStr, "hishtory fatal error", "Ran command, but hishtory had a fatal error!") @@ -105,7 +105,7 @@ func (z zshTester) RunInteractiveShellRelaxed(t testing.TB, script string) (stri cmd.Stderr = &stderr err := cmd.Run() if err != nil { - return stdout.String(), fmt.Errorf("unexpected error when running command=%#v, out=%#v, err=%#v: %v", script, stdout.String(), stderr.String(), err) + return stdout.String(), fmt.Errorf("unexpected error when running command=%#v, out=%#v, err=%#v: %w", script, stdout.String(), stderr.String(), err) } outStr := stdout.String() require.NotContains(t, outStr, "hishtory fatal error") diff --git a/client/cmd/install.go b/client/cmd/install.go index 8aa87bd..7314f6e 100644 --- a/client/cmd/install.go +++ b/client/cmd/install.go @@ -129,7 +129,7 @@ func warnIfUnsupportedBashVersion() error { cmd := exec.Command("bash", "--version") bashVersion, err := cmd.CombinedOutput() if err != nil { - return fmt.Errorf("failed to check bash version: %v", err) + return fmt.Errorf("failed to check bash version: %w", err) } if strings.Contains(string(bashVersion), "version 3.") { fmt.Printf("Warning: Your current bash version does not support overriding control-r. Please upgrade to at least bash 5 to enable the control-r integration.\n") @@ -140,7 +140,7 @@ func warnIfUnsupportedBashVersion() error { func install(secretKey string, offline bool) error { homedir, err := os.UserHomeDir() if err != nil { - return fmt.Errorf("failed to get user's home directory: %v", err) + return fmt.Errorf("failed to get user's home directory: %w", err) } err = hctx.MakeHishtoryDir() if err != nil { @@ -201,16 +201,16 @@ func installBinary(homedir string) (string, error) { if _, err := os.Stat(clientPath); err == nil { err = syscall.Unlink(clientPath) if err != nil { - return "", fmt.Errorf("failed to unlink %s for install: %v", clientPath, err) + return "", fmt.Errorf("failed to unlink %s for install: %w", clientPath, err) } } err = copyFile(os.Args[0], clientPath) if err != nil { - return "", fmt.Errorf("failed to copy hishtory binary to $PATH: %v", err) + return "", fmt.Errorf("failed to copy hishtory binary to $PATH: %w", err) } err = os.Chmod(clientPath, 0o700) if err != nil { - return "", fmt.Errorf("failed to set permissions on hishtory binary: %v", err) + return "", fmt.Errorf("failed to set permissions on hishtory binary: %w", err) } return clientPath, nil } @@ -236,12 +236,12 @@ func configureFish(homedir, binaryPath string) error { } err = os.WriteFile(getFishConfigPath(homedir), []byte(configContents), 0o644) if err != nil { - return fmt.Errorf("failed to write config.zsh file: %v", err) + return fmt.Errorf("failed to write config.zsh file: %w", err) } // Check if we need to configure the fishrc fishIsConfigured, err := isFishConfigured(homedir) if err != nil { - return fmt.Errorf("failed to check ~/.config/fish/config.fish: %v", err) + return fmt.Errorf("failed to check ~/.config/fish/config.fish: %w", err) } if fishIsConfigured { return nil @@ -249,7 +249,7 @@ func configureFish(homedir, binaryPath string) error { // Add to fishrc err = os.MkdirAll(path.Join(homedir, ".config/fish"), 0o744) if err != nil { - return fmt.Errorf("failed to create fish config directory: %v", err) + return fmt.Errorf("failed to create fish config directory: %w", err) } return addToShellConfig(path.Join(homedir, ".config/fish/config.fish"), getFishConfigFragment(homedir)) } @@ -265,7 +265,7 @@ func isFishConfigured(homedir string) (bool, error) { } fishConfig, err := os.ReadFile(path.Join(homedir, ".config/fish/config.fish")) if err != nil { - return false, fmt.Errorf("failed to read ~/.config/fish/config.fish: %v", err) + return false, fmt.Errorf("failed to read ~/.config/fish/config.fish: %w", err) } return strings.Contains(string(fishConfig), getFishConfigFragment(homedir)), nil } @@ -286,12 +286,12 @@ func configureZshrc(homedir, binaryPath string) error { } err := os.WriteFile(getZshConfigPath(homedir), []byte(configContents), 0o644) if err != nil { - return fmt.Errorf("failed to write config.zsh file: %v", err) + return fmt.Errorf("failed to write config.zsh file: %w", err) } // Check if we need to configure the zshrc zshIsConfigured, err := isZshConfigured(homedir) if err != nil { - return fmt.Errorf("failed to check .zshrc: %v", err) + return fmt.Errorf("failed to check .zshrc: %w", err) } if zshIsConfigured { return nil @@ -318,7 +318,7 @@ func isZshConfigured(homedir string) (bool, error) { } bashrc, err := os.ReadFile(getZshRcPath(homedir)) if err != nil { - return false, fmt.Errorf("failed to read zshrc: %v", err) + return false, fmt.Errorf("failed to read zshrc: %w", err) } return strings.Contains(string(bashrc), getZshConfigFragment(homedir)), nil } @@ -339,12 +339,12 @@ func configureBashrc(homedir, binaryPath string) error { } err := os.WriteFile(getBashConfigPath(homedir), []byte(configContents), 0o644) if err != nil { - return fmt.Errorf("failed to write config.sh file: %v", err) + return fmt.Errorf("failed to write config.sh file: %w", err) } // Check if we need to configure the bashrc and configure it if so bashRcIsConfigured, err := isBashRcConfigured(homedir) if err != nil { - return fmt.Errorf("failed to check ~/.bashrc: %v", err) + return fmt.Errorf("failed to check ~/.bashrc: %w", err) } if !bashRcIsConfigured { err = addToShellConfig(path.Join(homedir, ".bashrc"), getBashConfigFragment(homedir)) @@ -356,7 +356,7 @@ func configureBashrc(homedir, binaryPath string) error { if doesBashProfileNeedConfig(homedir) { bashProfileIsConfigured, err := isBashProfileConfigured(homedir) if err != nil { - return fmt.Errorf("failed to check ~/.bash_profile: %v", err) + return fmt.Errorf("failed to check ~/.bash_profile: %w", err) } if !bashProfileIsConfigured { err = addToShellConfig(path.Join(homedir, ".bash_profile"), getBashConfigFragment(homedir)) @@ -371,12 +371,12 @@ func configureBashrc(homedir, binaryPath string) error { func addToShellConfig(shellConfigPath, configFragment string) error { f, err := os.OpenFile(shellConfigPath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0o644) if err != nil { - return fmt.Errorf("failed to append to %s: %v", shellConfigPath, err) + return fmt.Errorf("failed to append to %s: %w", shellConfigPath, err) } defer f.Close() _, err = f.WriteString(configFragment) if err != nil { - return fmt.Errorf("failed to append to %s: %v", shellConfigPath, err) + return fmt.Errorf("failed to append to %s: %w", shellConfigPath, err) } return nil } @@ -392,7 +392,7 @@ func isBashRcConfigured(homedir string) (bool, error) { } bashrc, err := os.ReadFile(path.Join(homedir, ".bashrc")) if err != nil { - return false, fmt.Errorf("failed to read bashrc: %v", err) + return false, fmt.Errorf("failed to read bashrc: %w", err) } return strings.Contains(string(bashrc), getBashConfigFragment(homedir)), nil } @@ -418,7 +418,7 @@ func isBashProfileConfigured(homedir string) (bool, error) { } bashrc, err := os.ReadFile(path.Join(homedir, ".bash_profile")) if err != nil { - return false, fmt.Errorf("failed to read bash_profile: %v", err) + return false, fmt.Errorf("failed to read bash_profile: %w", err) } return strings.Contains(string(bashrc), getBashConfigFragment(homedir)), nil } diff --git a/client/cmd/redact.go b/client/cmd/redact.go index 53c3b96..be82c9f 100644 --- a/client/cmd/redact.go +++ b/client/cmd/redact.go @@ -50,7 +50,7 @@ func redact(ctx *context.Context, query string, force bool) error { reader := bufio.NewReader(os.Stdin) resp, err := reader.ReadString('\n') if err != nil { - return fmt.Errorf("failed to read response: %v", err) + return fmt.Errorf("failed to read response: %w", err) } if strings.TrimSpace(resp) != "y" { fmt.Printf("Aborting delete per user response of %#v\n", strings.TrimSpace(resp)) diff --git a/client/cmd/saveHistoryEntry.go b/client/cmd/saveHistoryEntry.go index 951fac9..c5d954c 100644 --- a/client/cmd/saveHistoryEntry.go +++ b/client/cmd/saveHistoryEntry.go @@ -58,7 +58,7 @@ func maybeUploadSkippedHistoryEntries(ctx *context.Context) error { query := fmt.Sprintf("after:%s", time.Unix(config.MissedUploadTimestamp, 0).Format("2006-01-02")) entries, err := lib.Search(ctx, db, query, 0) if err != nil { - return fmt.Errorf("failed to retrieve history entries that haven't been uploaded yet: %v", err) + return fmt.Errorf("failed to retrieve history entries that haven't been uploaded yet: %w", err) } hctx.GetLogger().Infof("Uploading %d history entries that previously failed to upload (query=%#v)\n", len(entries), query) jsonValue, err := lib.EncryptAndMarshal(config, entries) @@ -76,7 +76,7 @@ func maybeUploadSkippedHistoryEntries(ctx *context.Context) error { config.MissedUploadTimestamp = 0 err = hctx.SetConfig(config) if err != nil { - return fmt.Errorf("failed to mark a history entry as uploaded: %v", err) + return fmt.Errorf("failed to mark a history entry as uploaded: %w", err) } return nil } @@ -140,11 +140,11 @@ func saveHistoryEntry(ctx *context.Context) { if config.BetaMode { tx, err := lib.MakeWhereQueryFromSearch(ctx, db, "cwd:"+entry.CurrentWorkingDirectory+" start_time:"+strconv.FormatInt(entry.StartTime.Unix(), 10)) if err != nil { - lib.CheckFatalError(fmt.Errorf("failed to query for pre-saved history entries: %s", err)) + lib.CheckFatalError(fmt.Errorf("failed to query for pre-saved history entries: %w", err)) } res := tx.Delete(&data.HistoryEntry{}) if res.Error != nil { - lib.CheckFatalError(fmt.Errorf("failed to delete pre-saved history entries: %s", res.Error)) + lib.CheckFatalError(fmt.Errorf("failed to delete pre-saved history entries: %w", res.Error)) } if res.RowsAffected > 1 { lib.CheckFatalError(fmt.Errorf("attempted to delete pre-saved entry, but something went wrong since we deleted %d rows", res.RowsAffected)) @@ -224,14 +224,14 @@ func buildPreArgsHistoryEntry(ctx *context.Context) (*data.HistoryEntry, error) // user user, err := user.Current() if err != nil { - return nil, fmt.Errorf("failed to build history entry: %v", err) + return nil, fmt.Errorf("failed to build history entry: %w", err) } entry.LocalUsername = user.Username // cwd and homedir cwd, homedir, err := getCwd(ctx) if err != nil { - return nil, fmt.Errorf("failed to build history entry: %v", err) + return nil, fmt.Errorf("failed to build history entry: %w", err) } entry.CurrentWorkingDirectory = cwd entry.HomeDirectory = homedir @@ -239,7 +239,7 @@ func buildPreArgsHistoryEntry(ctx *context.Context) (*data.HistoryEntry, error) // hostname hostname, err := os.Hostname() if err != nil { - return nil, fmt.Errorf("failed to build history entry: %v", err) + return nil, fmt.Errorf("failed to build history entry: %w", err) } entry.Hostname = hostname @@ -272,14 +272,14 @@ func buildHistoryEntry(ctx *context.Context, args []string) (*data.HistoryEntry, // exitCode exitCode, err := strconv.Atoi(args[3]) if err != nil { - return nil, fmt.Errorf("failed to build history entry: %v", err) + return nil, fmt.Errorf("failed to build history entry: %w", err) } entry.ExitCode = exitCode // start time seconds, err := parseCrossPlatformInt(args[5]) if err != nil { - return nil, fmt.Errorf("failed to parse start time %s as int: %v", args[5], err) + return nil, fmt.Errorf("failed to parse start time %s as int: %w", args[5], err) } entry.StartTime = time.Unix(seconds, 0) @@ -290,11 +290,11 @@ func buildHistoryEntry(ctx *context.Context, args []string) (*data.HistoryEntry, if shell == "bash" { cmd, err := getLastCommand(args[4]) if err != nil { - return nil, fmt.Errorf("failed to build history entry: %v", err) + return nil, fmt.Errorf("failed to build history entry: %w", err) } shouldBeSkipped, err := shouldSkipHiddenCommand(ctx, args[4]) if err != nil { - return nil, fmt.Errorf("failed to check if command was hidden: %v", err) + return nil, fmt.Errorf("failed to check if command was hidden: %w", err) } if shouldBeSkipped || strings.HasPrefix(cmd, " ") { // Don't save commands that start with a space @@ -443,7 +443,7 @@ func maybeSkipBashHistTimePrefix(cmdLine string) (string, error) { } re, err := regexp.Compile("^" + buildRegexFromTimeFormat(format)) if err != nil { - return "", fmt.Errorf("failed to parse regex for HISTTIMEFORMAT variable: %v", err) + return "", fmt.Errorf("failed to parse regex for HISTTIMEFORMAT variable: %w", err) } return re.ReplaceAllLiteralString(cmdLine, ""), nil } @@ -481,7 +481,7 @@ func shouldSkipHiddenCommand(ctx *context.Context, historyLine string) (bool, er func getCwd(ctx *context.Context) (string, string, error) { cwd, err := getCwdWithoutSubstitution() if err != nil { - return "", "", fmt.Errorf("failed to get cwd for last command: %v", err) + return "", "", fmt.Errorf("failed to get cwd for last command: %w", err) } homedir := hctx.GetHome(ctx) if cwd == homedir { diff --git a/client/data/data.go b/client/data/data.go index 4406b4d..17d3f70 100644 --- a/client/data/data.go +++ b/client/data/data.go @@ -96,16 +96,16 @@ func makeAead(userSecret string) (cipher.AEAD, error) { func Encrypt(userSecret string, data, additionalData []byte) ([]byte, []byte, error) { aead, err := makeAead(userSecret) if err != nil { - return []byte{}, []byte{}, fmt.Errorf("failed to make AEAD: %v", err) + return []byte{}, []byte{}, fmt.Errorf("failed to make AEAD: %w", err) } nonce := make([]byte, 12) if _, err := io.ReadFull(rand.Reader, nonce); err != nil { - return []byte{}, []byte{}, fmt.Errorf("failed to read a nonce: %v", err) + return []byte{}, []byte{}, fmt.Errorf("failed to read a nonce: %w", err) } ciphertext := aead.Seal(nil, nonce, data, additionalData) _, err = aead.Open(nil, nonce, ciphertext, additionalData) if err != nil { - return []byte{}, []byte{}, fmt.Errorf("failed to open AEAD: %v", err) + return []byte{}, []byte{}, fmt.Errorf("failed to open AEAD: %w", err) } return ciphertext, nonce, nil } @@ -113,11 +113,11 @@ func Encrypt(userSecret string, data, additionalData []byte) ([]byte, []byte, er func Decrypt(userSecret string, data, additionalData, nonce []byte) ([]byte, error) { aead, err := makeAead(userSecret) if err != nil { - return []byte{}, fmt.Errorf("failed to make AEAD: %v", err) + return []byte{}, fmt.Errorf("failed to make AEAD: %w", err) } plaintext, err := aead.Open(nil, nonce, data, additionalData) if err != nil { - return []byte{}, fmt.Errorf("failed to decrypt: %v", err) + return []byte{}, fmt.Errorf("failed to decrypt: %w", err) } return plaintext, nil } diff --git a/client/hctx/hctx.go b/client/hctx/hctx.go index 7536bc7..bba8aa3 100644 --- a/client/hctx/hctx.go +++ b/client/hctx/hctx.go @@ -30,7 +30,7 @@ func GetLogger() *logrus.Logger { getLoggerOnce.Do(func() { homedir, err := os.UserHomeDir() if err != nil { - panic(fmt.Errorf("failed to get user's home directory: %v", err)) + panic(fmt.Errorf("failed to get user's home directory: %w", err)) } err = MakeHishtoryDir() if err != nil { @@ -59,11 +59,11 @@ func GetLogger() *logrus.Logger { func MakeHishtoryDir() error { homedir, err := os.UserHomeDir() if err != nil { - return fmt.Errorf("failed to get user's home directory: %v", err) + return fmt.Errorf("failed to get user's home directory: %w", err) } err = os.MkdirAll(path.Join(homedir, data.GetHishtoryPath()), 0o744) if err != nil { - return fmt.Errorf("failed to create ~/%s dir: %v", data.GetHishtoryPath(), err) + return fmt.Errorf("failed to create ~/%s dir: %w", data.GetHishtoryPath(), err) } return nil } @@ -71,7 +71,7 @@ func MakeHishtoryDir() error { func OpenLocalSqliteDb() (*gorm.DB, error) { homedir, err := os.UserHomeDir() if err != nil { - return nil, fmt.Errorf("failed to get user's home directory: %v", err) + return nil, fmt.Errorf("failed to get user's home directory: %w", err) } err = MakeHishtoryDir() if err != nil { @@ -90,7 +90,7 @@ func OpenLocalSqliteDb() (*gorm.DB, error) { dsn := fmt.Sprintf("file:%s?mode=rwc&_journal_mode=WAL", dbFilePath) db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{SkipDefaultTransaction: true, Logger: newLogger}) if err != nil { - return nil, fmt.Errorf("failed to connect to the DB: %v", err) + return nil, fmt.Errorf("failed to connect to the DB: %w", err) } tx, err := db.DB() if err != nil { @@ -112,17 +112,17 @@ func MakeContext() *context.Context { ctx := context.Background() config, err := GetConfig() if err != nil { - panic(fmt.Errorf("failed to retrieve config: %v", err)) + panic(fmt.Errorf("failed to retrieve config: %w", err)) } ctx = context.WithValue(ctx, hishtoryContextKey("config"), config) db, err := OpenLocalSqliteDb() if err != nil { - panic(fmt.Errorf("failed to open local DB: %v", err)) + panic(fmt.Errorf("failed to open local DB: %w", err)) } ctx = context.WithValue(ctx, hishtoryContextKey("db"), db) homedir, err := os.UserHomeDir() if err != nil { - panic(fmt.Errorf("failed to get homedir: %v", err)) + panic(fmt.Errorf("failed to get homedir: %w", err)) } ctx = context.WithValue(ctx, hishtoryContextKey("homedir"), homedir) return &ctx @@ -191,20 +191,20 @@ type CustomColumnDefinition struct { func GetConfigContents() ([]byte, error) { homedir, err := os.UserHomeDir() if err != nil { - return nil, fmt.Errorf("failed to retrieve homedir: %v", err) + return nil, fmt.Errorf("failed to retrieve homedir: %w", err) } dat, err := os.ReadFile(path.Join(homedir, data.GetHishtoryPath(), data.CONFIG_PATH)) if err != nil { files, err := os.ReadDir(path.Join(homedir, data.GetHishtoryPath())) if err != nil { - return nil, fmt.Errorf("failed to read config file (and failed to list too): %v", err) + return nil, fmt.Errorf("failed to read config file (and failed to list too): %w", err) } filenames := "" for _, file := range files { filenames += file.Name() filenames += ", " } - return nil, fmt.Errorf("failed to read config file (files in HISHTORY_PATH: %s): %v", filenames, err) + return nil, fmt.Errorf("failed to read config file (files in HISHTORY_PATH: %s): %w", filenames, err) } return dat, nil } @@ -217,7 +217,7 @@ func GetConfig() (ClientConfig, error) { var config ClientConfig err = json.Unmarshal(data, &config) if err != nil { - return ClientConfig{}, fmt.Errorf("failed to parse config file: %v", err) + return ClientConfig{}, fmt.Errorf("failed to parse config file: %w", err) } if config.DisplayedColumns == nil || len(config.DisplayedColumns) == 0 { config.DisplayedColumns = []string{"Hostname", "CWD", "Timestamp", "Runtime", "Exit Code", "Command"} @@ -231,11 +231,11 @@ func GetConfig() (ClientConfig, error) { func SetConfig(config ClientConfig) error { serializedConfig, err := json.Marshal(config) if err != nil { - return fmt.Errorf("failed to serialize config: %v", err) + return fmt.Errorf("failed to serialize config: %w", err) } homedir, err := os.UserHomeDir() if err != nil { - return fmt.Errorf("failed to retrieve homedir: %v", err) + return fmt.Errorf("failed to retrieve homedir: %w", err) } err = MakeHishtoryDir() if err != nil { @@ -245,11 +245,11 @@ func SetConfig(config ClientConfig) error { stagedConfigPath := configPath + ".tmp-" + uuid.Must(uuid.NewRandom()).String() err = os.WriteFile(stagedConfigPath, serializedConfig, 0o644) if err != nil { - return fmt.Errorf("failed to write config: %v", err) + return fmt.Errorf("failed to write config: %w", err) } err = os.Rename(stagedConfigPath, configPath) if err != nil { - return fmt.Errorf("failed to replace config file with the updated version: %v", err) + return fmt.Errorf("failed to replace config file with the updated version: %w", err) } return nil } diff --git a/client/lib/lib.go b/client/lib/lib.go index 0064f5a..a8d15ad 100644 --- a/client/lib/lib.go +++ b/client/lib/lib.go @@ -66,7 +66,7 @@ func Setup(userSecret string, isOffline bool) error { config.IsOffline = isOffline err := hctx.SetConfig(config) if err != nil { - return fmt.Errorf("failed to persist config to disk: %v", err) + return fmt.Errorf("failed to persist config to disk: %w", err) } // Drop all existing data @@ -82,22 +82,22 @@ func Setup(userSecret string, isOffline bool) error { } _, err = ApiGet("/api/v1/register?user_id=" + data.UserId(userSecret) + "&device_id=" + config.DeviceId) if err != nil { - return fmt.Errorf("failed to register device with backend: %v", err) + return fmt.Errorf("failed to register device with backend: %w", err) } respBody, err := ApiGet("/api/v1/bootstrap?user_id=" + data.UserId(userSecret) + "&device_id=" + config.DeviceId) if err != nil { - return fmt.Errorf("failed to bootstrap device from the backend: %v", err) + return fmt.Errorf("failed to bootstrap device from the backend: %w", err) } var retrievedEntries []*shared.EncHistoryEntry err = json.Unmarshal(respBody, &retrievedEntries) if err != nil { - return fmt.Errorf("failed to load JSON response: %v", err) + return fmt.Errorf("failed to load JSON response: %w", err) } for _, entry := range retrievedEntries { decEntry, err := data.DecryptHistoryEntry(userSecret, *entry) if err != nil { - return fmt.Errorf("failed to decrypt history entry from server: %v", err) + return fmt.Errorf("failed to decrypt history entry from server: %w", err) } AddToDbIfNew(db, decEntry) } @@ -252,30 +252,30 @@ func ImportHistory(ctx *context.Context, shouldReadStdin, force bool) (int, erro bashHistPath := filepath.Join(homedir, ".bash_history") historyEntries, err := readFileToArray(bashHistPath) if err != nil { - return 0, fmt.Errorf("failed to parse bash history: %v", err) + return 0, fmt.Errorf("failed to parse bash history: %w", err) } zshHistPath := filepath.Join(homedir, ".zsh_history") extraEntries, err := readFileToArray(zshHistPath) if err != nil { - return 0, fmt.Errorf("failed to parse zsh history: %v", err) + return 0, fmt.Errorf("failed to parse zsh history: %w", err) } historyEntries = append(historyEntries, extraEntries...) extraEntries, err = parseFishHistory(homedir) if err != nil { - return 0, fmt.Errorf("failed to parse fish history: %v", err) + return 0, fmt.Errorf("failed to parse fish history: %w", err) } historyEntries = append(historyEntries, extraEntries...) if histfile := os.Getenv("HISTFILE"); histfile != "" && histfile != zshHistPath && histfile != bashHistPath { extraEntries, err := readFileToArray(histfile) if err != nil { - return 0, fmt.Errorf("failed to parse histfile: %v", err) + return 0, fmt.Errorf("failed to parse histfile: %w", err) } historyEntries = append(historyEntries, extraEntries...) } if shouldReadStdin { extraEntries, err = readStdin() if err != nil { - return 0, fmt.Errorf("failed to read stdin: %v", err) + return 0, fmt.Errorf("failed to read stdin: %w", err) } historyEntries = append(historyEntries, extraEntries...) } @@ -307,17 +307,17 @@ func ImportHistory(ctx *context.Context, shouldReadStdin, force bool) (int, erro } err = ReliableDbCreate(db, entry) if err != nil { - return 0, fmt.Errorf("failed to insert imported history entry: %v", err) + return 0, fmt.Errorf("failed to insert imported history entry: %w", err) } } err = Reupload(ctx) if err != nil { - return 0, fmt.Errorf("failed to upload hishtory import: %v", err) + return 0, fmt.Errorf("failed to upload hishtory import: %w", err) } config.HaveCompletedInitialImport = true err = hctx.SetConfig(config) if err != nil { - return 0, fmt.Errorf("failed to mark initial import as completed, this may lead to duplicate history entries: %v", err) + return 0, fmt.Errorf("failed to mark initial import as completed, this may lead to duplicate history entries: %w", err) } // Trigger a checkpoint so that these bulk entries are added from the WAL to the main DB db.Exec("PRAGMA wal_checkpoint") @@ -386,12 +386,12 @@ func readFileToArray(path string) ([]string, error) { func GetDownloadData() (shared.UpdateInfo, error) { respBody, err := ApiGet("/api/v1/download") if err != nil { - return shared.UpdateInfo{}, fmt.Errorf("failed to download update info: %v", err) + return shared.UpdateInfo{}, fmt.Errorf("failed to download update info: %w", err) } var downloadData shared.UpdateInfo err = json.Unmarshal(respBody, &downloadData) if err != nil { - return shared.UpdateInfo{}, fmt.Errorf("failed to parse update info: %v", err) + return shared.UpdateInfo{}, fmt.Errorf("failed to parse update info: %w", err) } return downloadData, nil } @@ -438,7 +438,7 @@ func Update(ctx *context.Context) error { homedir := hctx.GetHome(ctx) err = syscall.Unlink(path.Join(homedir, data.GetHishtoryPath(), "hishtory")) if err != nil { - return fmt.Errorf("failed to unlink %s for update: %v", path.Join(homedir, data.GetHishtoryPath(), "hishtory"), err) + return fmt.Errorf("failed to unlink %s for update: %w", path.Join(homedir, data.GetHishtoryPath(), "hishtory"), err) } } @@ -450,7 +450,7 @@ func Update(ctx *context.Context) error { cmd.Stderr = &stderr err = cmd.Run() if err != nil { - return fmt.Errorf("failed to chmod +x the update (stdout=%#v, stderr=%#v): %v", stdout.String(), stderr.String(), err) + return fmt.Errorf("failed to chmod +x the update (stdout=%#v, stderr=%#v): %w", stdout.String(), stderr.String(), err) } cmd = exec.Command(getTmpClientPath(), "install") cmd.Stdout = os.Stdout @@ -458,7 +458,7 @@ func Update(ctx *context.Context) error { cmd.Stdin = os.Stdin err = cmd.Run() if err != nil { - return fmt.Errorf("failed to install update (stderr=%#v), is %s in a noexec directory? (if so, set the TMPDIR environment variable): %v", stderr.String(), getTmpClientPath(), err) + return fmt.Errorf("failed to install update (stderr=%#v), is %s in a noexec directory? (if so, set the TMPDIR environment variable): %w", stderr.String(), getTmpClientPath(), err) } fmt.Printf("Successfully updated hishtory from v0.%s to %s\n", Version, downloadData.Version) return nil @@ -550,7 +550,7 @@ func stripCodeSignature(inPath, outPath string) error { cmd.Stderr = &stderr err = cmd.Run() if err != nil { - return fmt.Errorf("failed to use codesign_allocate to strip signatures on binary=%v (stdout=%#v, stderr%#v): %v", inPath, stdout.String(), stderr.String(), err) + return fmt.Errorf("failed to use codesign_allocate to strip signatures on binary=%v (stdout=%#v, stderr%#v): %w", inPath, stdout.String(), stderr.String(), err) } return nil } @@ -591,7 +591,7 @@ func downloadFile(filename, url string) error { // Download the data resp, err := http.Get(url) if err != nil { - return fmt.Errorf("failed to download file at %s to %s: %v", url, filename, err) + return fmt.Errorf("failed to download file at %s to %s: %w", url, filename, err) } defer resp.Body.Close() if resp.StatusCode != 200 { @@ -609,7 +609,7 @@ func downloadFile(filename, url string) error { // Create the file out, err := os.Create(filename) if err != nil { - return fmt.Errorf("failed to save file to %s: %v", filename, err) + return fmt.Errorf("failed to save file to %s: %w", filename, err) } defer out.Close() @@ -636,12 +636,12 @@ func ApiGet(path string) ([]byte, error) { start := time.Now() req, err := http.NewRequest("GET", getServerHostname()+path, nil) if err != nil { - return nil, fmt.Errorf("failed to create GET: %v", err) + return nil, fmt.Errorf("failed to create GET: %w", err) } req.Header.Set("X-Hishtory-Version", "v0."+Version) resp, err := httpClient().Do(req) if err != nil { - return nil, fmt.Errorf("failed to GET %s%s: %v", getServerHostname(), path, err) + return nil, fmt.Errorf("failed to GET %s%s: %w", getServerHostname(), path, err) } defer resp.Body.Close() if resp.StatusCode != 200 { @@ -649,7 +649,7 @@ func ApiGet(path string) ([]byte, error) { } respBody, err := io.ReadAll(resp.Body) if err != nil { - return nil, fmt.Errorf("failed to read response body from GET %s%s: %v", getServerHostname(), path, err) + return nil, fmt.Errorf("failed to read response body from GET %s%s: %w", getServerHostname(), path, err) } duration := time.Since(start) hctx.GetLogger().Infof("ApiGet(%#v): %s\n", path, duration.String()) @@ -663,13 +663,13 @@ func ApiPost(path, contentType string, data []byte) ([]byte, error) { start := time.Now() req, err := http.NewRequest("POST", getServerHostname()+path, bytes.NewBuffer(data)) if err != nil { - return nil, fmt.Errorf("failed to create POST: %v", err) + return nil, fmt.Errorf("failed to create POST: %w", err) } req.Header.Set("Content-Type", contentType) req.Header.Set("X-Hishtory-Version", "v0."+Version) resp, err := httpClient().Do(req) if err != nil { - return nil, fmt.Errorf("failed to POST %s: %v", path, err) + return nil, fmt.Errorf("failed to POST %s: %w", path, err) } defer resp.Body.Close() if resp.StatusCode != 200 { @@ -677,7 +677,7 @@ func ApiPost(path, contentType string, data []byte) ([]byte, error) { } respBody, err := io.ReadAll(resp.Body) if err != nil { - return nil, fmt.Errorf("failed to read response body from POST %s: %v", path, err) + return nil, fmt.Errorf("failed to read response body from POST %s: %w", path, err) } duration := time.Since(start) hctx.GetLogger().Infof("ApiPost(%#v): %s\n", path, duration.String()) @@ -721,13 +721,13 @@ func ReliableDbCreate(db *gorm.DB, entry interface{}) error { return nil } } - return fmt.Errorf("unrecoverable sqlite error: %v", err) + return fmt.Errorf("unrecoverable sqlite error: %w", err) } if err != nil && err.Error() != "database is locked (5) (SQLITE_BUSY)" { - return fmt.Errorf("unrecoverable sqlite error: %v", err) + return fmt.Errorf("unrecoverable sqlite error: %w", err) } } - return fmt.Errorf("failed to create DB entry even with %d retries: %v", i, err) + return fmt.Errorf("failed to create DB entry even with %d retries: %w", i, err) } func EncryptAndMarshal(config hctx.ClientConfig, entries []*data.HistoryEntry) ([]byte, error) { @@ -742,7 +742,7 @@ func EncryptAndMarshal(config hctx.ClientConfig, entries []*data.HistoryEntry) ( } jsonValue, err := json.Marshal(encEntries) if err != nil { - return jsonValue, fmt.Errorf("failed to marshal encrypted history entry: %v", err) + return jsonValue, fmt.Errorf("failed to marshal encrypted history entry: %w", err) } return jsonValue, nil } @@ -754,16 +754,16 @@ func Reupload(ctx *context.Context) error { } entries, err := Search(ctx, hctx.GetDb(ctx), "", 0) if err != nil { - return fmt.Errorf("failed to reupload due to failed search: %v", err) + return fmt.Errorf("failed to reupload due to failed search: %w", err) } for _, chunk := range shared.Chunks(entries, 100) { jsonValue, err := EncryptAndMarshal(config, chunk) if err != nil { - return fmt.Errorf("failed to reupload due to failed encryption: %v", err) + return fmt.Errorf("failed to reupload due to failed encryption: %w", err) } _, err = ApiPost("/api/v1/submit?source_device_id="+config.DeviceId, "application/json", jsonValue) if err != nil { - return fmt.Errorf("failed to reupload due to failed POST: %v", err) + return fmt.Errorf("failed to reupload due to failed POST: %w", err) } } return nil @@ -785,12 +785,12 @@ func RetrieveAdditionalEntriesFromRemote(ctx *context.Context) error { var retrievedEntries []*shared.EncHistoryEntry err = json.Unmarshal(respBody, &retrievedEntries) if err != nil { - return fmt.Errorf("failed to load JSON response: %v", err) + return fmt.Errorf("failed to load JSON response: %w", err) } for _, entry := range retrievedEntries { decEntry, err := data.DecryptHistoryEntry(config.UserSecret, *entry) if err != nil { - return fmt.Errorf("failed to decrypt history entry from server: %v", err) + return fmt.Errorf("failed to decrypt history entry from server: %w", err) } AddToDbIfNew(db, decEntry) } @@ -819,7 +819,7 @@ func ProcessDeletionRequests(ctx *context.Context) error { for _, entry := range request.Messages.Ids { res := db.Where("device_id = ? AND end_time = ?", entry.DeviceId, entry.Date).Delete(&data.HistoryEntry{}) if res.Error != nil { - return fmt.Errorf("DB error: %v", res.Error) + return fmt.Errorf("DB error: %w", res.Error) } } } @@ -843,7 +843,7 @@ func parseTimeGenerously(input string) (time.Time, error) { func MakeWhereQueryFromSearch(ctx *context.Context, db *gorm.DB, query string) (*gorm.DB, error) { tokens, err := tokenize(query) if err != nil { - return nil, fmt.Errorf("failed to tokenize query: %v", err) + return nil, fmt.Errorf("failed to tokenize query: %w", err) } tx := db.Model(&data.HistoryEntry{}).Where("true") for _, token := range tokens { @@ -903,7 +903,7 @@ func Search(ctx *context.Context, db *gorm.DB, query string, limit int) ([]*data var historyEntries []*data.HistoryEntry result := tx.Find(&historyEntries) if result.Error != nil { - return nil, fmt.Errorf("DB query error: %v", result.Error) + return nil, fmt.Errorf("DB query error: %w", result.Error) } return historyEntries, nil } @@ -931,13 +931,13 @@ func parseAtomizedToken(ctx *context.Context, token string) (string, interface{} case "before": t, err := parseTimeGenerously(val) if err != nil { - return "", nil, nil, fmt.Errorf("failed to parse before:%s as a timestamp: %v", val, err) + return "", nil, nil, fmt.Errorf("failed to parse before:%s as a timestamp: %w", val, err) } return "(CAST(strftime(\"%s\",start_time) AS INTEGER) < ?)", t.Unix(), nil, nil case "after": t, err := parseTimeGenerously(val) if err != nil { - return "", nil, nil, fmt.Errorf("failed to parse after:%s as a timestamp: %v", val, err) + return "", nil, nil, fmt.Errorf("failed to parse after:%s as a timestamp: %w", val, err) } return "(CAST(strftime(\"%s\",start_time) AS INTEGER) > ?)", t.Unix(), nil, nil case "start_time": @@ -945,7 +945,7 @@ func parseAtomizedToken(ctx *context.Context, token string) (string, interface{} // internally for pre-saving history entries. t, err := parseTimeGenerously(val) if err != nil { - return "", nil, nil, fmt.Errorf("failed to parse after:%s as a timestamp: %v", val, err) + return "", nil, nil, fmt.Errorf("failed to parse after:%s as a timestamp: %w", val, err) } return "(CAST(strftime(\"%s\",start_time) AS INTEGER) = ?)", t.Unix(), nil, nil case "command": @@ -960,7 +960,7 @@ func parseAtomizedToken(ctx *context.Context, token string) (string, interface{} // Also get all ones that are in the DB names, err := getAllCustomColumnNames(ctx) if err != nil { - return "", nil, nil, fmt.Errorf("failed to get custom column names from the DB: %v", err) + return "", nil, nil, fmt.Errorf("failed to get custom column names from the DB: %w", err) } knownCustomColumns = append(knownCustomColumns, names...) // Check if the atom is for a custom column that exists and if it isn't, return an error @@ -1079,7 +1079,7 @@ func SendDeletionRequest(deletionRequest shared.DeletionRequest) error { } _, err = ApiPost("/api/v1/add-deletion-request", "application/json", data) if err != nil { - return fmt.Errorf("failed to send deletion request to backend service, this may cause commands to not get deleted on other instances of hishtory: %v", err) + return fmt.Errorf("failed to send deletion request to backend service, this may cause commands to not get deleted on other instances of hishtory: %w", err) } return nil } diff --git a/client/lib/slsa.go b/client/lib/slsa.go index faeb973..13cadb7 100644 --- a/client/lib/slsa.go +++ b/client/lib/slsa.go @@ -61,7 +61,7 @@ func verifyBinary(ctx *context.Context, binaryPath, attestationPath, versionTag attestation, err := os.ReadFile(attestationPath) if err != nil { - return fmt.Errorf("failed to read attestation file: %v", err) + return fmt.Errorf("failed to read attestation file: %w", err) } hash, err := getFileHash(binaryPath) @@ -75,13 +75,13 @@ func verifyBinary(ctx *context.Context, binaryPath, attestationPath, versionTag func getFileHash(binaryPath string) (string, error) { binaryFile, err := os.Open(binaryPath) if err != nil { - return "", fmt.Errorf("failed to read binary for verification purposes: %v", err) + return "", fmt.Errorf("failed to read binary for verification purposes: %w", err) } defer binaryFile.Close() hasher := sha256.New() if _, err := io.Copy(hasher, binaryFile); err != nil { - return "", fmt.Errorf("failed to hash binary: %v", err) + return "", fmt.Errorf("failed to hash binary: %w", err) } hash := hex.EncodeToString(hasher.Sum(nil)) return hash, nil @@ -95,5 +95,5 @@ func handleSlsaFailure(srcErr error) error { fmt.Println("Proceeding with update...") return nil } - return fmt.Errorf("failed to verify SLSA provenance of the updated binary, aborting update (to bypass, set `export HISHTORY_DISABLE_SLSA_ATTESTATION=true`): %v", srcErr) + return fmt.Errorf("failed to verify SLSA provenance of the updated binary, aborting update (to bypass, set `export HISHTORY_DISABLE_SLSA_ATTESTATION=true`): %w", srcErr) } diff --git a/client/lib/tui.go b/client/lib/tui.go index 4be3f07..b8506c0 100644 --- a/client/lib/tui.go +++ b/client/lib/tui.go @@ -381,7 +381,7 @@ func getRows(ctx *context.Context, columnNames []string, query string, numEntrie entry.Command = strings.ReplaceAll(entry.Command, "\n", "\\n") row, err := buildTableRow(ctx, columnNames, *entry) if err != nil { - return nil, nil, fmt.Errorf("failed to build row for entry=%#v: %v", entry, err) + return nil, nil, fmt.Errorf("failed to build row for entry=%#v: %w", entry, err) } rows = append(rows, row) filteredData = append(filteredData, entry) @@ -449,7 +449,7 @@ func makeTableColumns(ctx *context.Context, columnNames []string, rows []table.R // Get the actual terminal width. If we're below this, opportunistically add some padding aiming for the maximum column widths terminalWidth, _, err := getTerminalSize() if err != nil { - return nil, fmt.Errorf("failed to get terminal size: %v", err) + return nil, fmt.Errorf("failed to get terminal size: %w", err) } for totalWidth < (terminalWidth - len(columnNames)) { prevTotalWidth := totalWidth