Merge branch 'master' into import

This commit is contained in:
David Dworken 2024-08-25 11:58:18 -07:00 committed by GitHub
commit df6c853fdb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 209 additions and 60 deletions

View File

@ -46,6 +46,7 @@ jobs:
with: with:
go-version: 1.21 go-version: 1.21
- name: Go test - name: Go test
if: ${{ !startsWith(github.event.head_commit.message, 'Release') }}
env: env:
DD_API_KEY: ${{ secrets.DD_API_KEY }} DD_API_KEY: ${{ secrets.DD_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}

View File

@ -19,6 +19,8 @@ jobs:
go install honnef.co/go/tools/cmd/staticcheck@latest go install honnef.co/go/tools/cmd/staticcheck@latest
go install github.com/kisielk/errcheck@latest go install github.com/kisielk/errcheck@latest
go install mvdan.cc/gofumpt@latest
go install github.com/daixiang0/gci@latest
- uses: pre-commit/action@v3.0.0 - uses: pre-commit/action@v3.0.0
with: with:
extra_args: --all-files extra_args: --all-files

3
.gitignore vendored
View File

@ -9,3 +9,6 @@ node_modules/
package.json package.json
package-lock.json package-lock.json
.prettierrc .prettierrc
# VS Code settings
.vscode/

View File

@ -1 +1 @@
302 304

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"database/sql" "database/sql"
"fmt" "fmt"
"os"
"strings" "strings"
"time" "time"
@ -11,6 +12,7 @@ import (
"github.com/jackc/pgx/v4/stdlib" "github.com/jackc/pgx/v4/stdlib"
_ "github.com/lib/pq" _ "github.com/lib/pq"
"github.com/samber/lo"
sqltrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/database/sql" sqltrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/database/sql"
gormtrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/gorm.io/gorm.v1" gormtrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/gorm.io/gorm.v1"
"gorm.io/driver/postgres" "gorm.io/driver/postgres"
@ -238,31 +240,42 @@ func (db *DB) UninstallDevice(ctx context.Context, userId, deviceId string) (int
} }
func (db *DB) DeleteMessagesFromBackend(ctx context.Context, userId string, deletedMessages []shared.MessageIdentifier) (int64, error) { func (db *DB) DeleteMessagesFromBackend(ctx context.Context, userId string, deletedMessages []shared.MessageIdentifier) (int64, error) {
tx := db.WithContext(ctx).Where("false") if len(deletedMessages) == 0 {
for _, message := range deletedMessages { return 0, nil
if userId == "" {
return 0, fmt.Errorf("failed to delete entry because userId is empty")
}
if message.DeviceId == "" {
// The DeviceId is empty, so there is nothing to do for this since it won't match anything
continue
}
if message.EndTime != (time.Time{}) && message.EntryId != "" {
// Note that we do an OR with date or the ID matching since the ID is not always recorded for older history entries.
tx = tx.Or(db.WithContext(ctx).Where("user_id = ? AND (date = ? OR encrypted_id = ?)", userId, message.EndTime, message.EntryId))
} else if message.EndTime != (time.Time{}) && message.EntryId == "" {
tx = tx.Or(db.WithContext(ctx).Where("user_id = ? AND (date = ?)", userId, message.EndTime))
} else if message.EndTime == (time.Time{}) && message.EntryId != "" {
tx = tx.Or(db.WithContext(ctx).Where("user_id = ? AND (encrypted_id = ?)", userId, message.EntryId))
} else {
return 0, fmt.Errorf("failed to delete entry because message.EndTime=%#v and message.EntryId=%#v are both empty", message.EndTime, message.EntryId)
}
} }
result := tx.Delete(&shared.EncHistoryEntry{})
if result.Error != nil { if userId == "" {
return 0, fmt.Errorf("result.Error: %w", result.Error) return 0, fmt.Errorf("failed to delete entry because userId is empty")
} }
return result.RowsAffected, nil
var rowsAffected int64
for _, chunkOfMessages := range lo.Chunk(deletedMessages, 255) {
tx := db.WithContext(ctx).Where("false")
for _, message := range chunkOfMessages {
if message.DeviceId == "" {
// The DeviceId is empty, so there is nothing to do for this since it won't match anything
continue
}
if message.EndTime != (time.Time{}) && message.EntryId != "" {
// Note that we do an OR with date or the ID matching since the ID is not always recorded for older history entries.
tx = tx.Or(db.WithContext(ctx).Where("user_id = ? AND (date = ? OR encrypted_id = ?)", userId, message.EndTime, message.EntryId))
} else if message.EndTime != (time.Time{}) && message.EntryId == "" {
tx = tx.Or(db.WithContext(ctx).Where("user_id = ? AND (date = ?)", userId, message.EndTime))
} else if message.EndTime == (time.Time{}) && message.EntryId != "" {
tx = tx.Or(db.WithContext(ctx).Where("user_id = ? AND (encrypted_id = ?)", userId, message.EntryId))
} else {
return 0, fmt.Errorf("failed to delete entry because message.EndTime=%#v and message.EntryId=%#v are both empty", message.EndTime, message.EntryId)
}
}
result := tx.Delete(&shared.EncHistoryEntry{})
if result.Error != nil {
return 0, fmt.Errorf("result.Error: %w", result.Error)
}
rowsAffected += result.RowsAffected
}
return rowsAffected, nil
} }
func (db *DB) DeletionRequestInc(ctx context.Context, userID, deviceID string) error { func (db *DB) DeletionRequestInc(ctx context.Context, userID, deviceID string) error {
@ -415,6 +428,56 @@ func (db *DB) GenerateAndStoreActiveUserStats(ctx context.Context) error {
}).Error }).Error
} }
func (db *DB) SelfHostedDeepClean(ctx context.Context) error {
return db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
runDeletes := os.Getenv("HISHTORY_SELF_HOSTED_DEEP_CLEAN") != ""
r := tx.Exec(`
CREATE TEMP TABLE temp_inactive_devices AS (
SELECT device_id
FROM usage_data
WHERE last_used <= (now() - INTERVAL '90 days')
)
`)
if r.Error != nil {
return fmt.Errorf("failed to create list of inactive users: %w", r.Error)
}
if runDeletes {
r = tx.Raw(`
DELETE FROM enc_history_entries WHERE
device_id IN (SELECT * FROM temp_inactive_devices)
`)
if r.Error != nil {
return fmt.Errorf("failed to delete entries for inactive devices: %w", r.Error)
}
r = tx.Raw(`
DELETE FROM devices WHERE
device_id IN (SELECT * FROM temp_inactive_devices)
`)
if r.Error != nil {
return fmt.Errorf("failed to delete inactive devices: %w", r.Error)
}
} else {
r = tx.Raw(`
SELECT COUNT(*)
FROM enc_history_entries
WHERE device_id IN (SELECT * FROM temp_inactive_devices)
`)
if r.Error != nil {
return fmt.Errorf("failed to count entries for inactive devices: %w", r.Error)
}
count, err := extractInt64FromRow(r.Row())
if err != nil {
return fmt.Errorf("failed to extract count of entries for inactive devices: %w", err)
}
if count > 10_000 {
fmt.Printf("WARNING: This server is persisting %d entries for devices that have been offline for more than 90 days. If this is unexpected, set the server environment variable HISHTORY_SELF_HOSTED_DEEP_CLEAN=1 to permanently delete these devices.\n", count)
}
}
fmt.Println("Successfully checked for inactive devices")
return nil
})
}
func (db *DB) DeepClean(ctx context.Context) error { func (db *DB) DeepClean(ctx context.Context) error {
err := db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { err := db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
// Delete entries for users that have one device and are inactive // Delete entries for users that have one device and are inactive

View File

@ -128,10 +128,17 @@ func cron(ctx context.Context, db *database.DB, stats *statsd.Client) error {
} }
// Run a deep clean less often to cover some more edge cases that hurt DB performance // Run a deep clean less often to cover some more edge cases that hurt DB performance
if isProductionEnvironment() && time.Since(LAST_DEEP_CLEAN) > 24*3*time.Hour { if time.Since(LAST_DEEP_CLEAN) > 24*3*time.Hour {
LAST_DEEP_CLEAN = time.Now() LAST_DEEP_CLEAN = time.Now()
if err := db.DeepClean(ctx); err != nil { if isProductionEnvironment() {
return fmt.Errorf("db.DeepClean: %w", err) if err := db.DeepClean(ctx); err != nil {
return fmt.Errorf("db.DeepClean: %w", err)
}
}
if !isProductionEnvironment() && !isTestEnvironment() {
if err := db.SelfHostedDeepClean(ctx); err != nil {
return fmt.Errorf("db.SelfHostedDeepClean: %w", err)
}
} }
} }

View File

@ -3265,4 +3265,29 @@ func TestInstallSkipConfigModification(t *testing.T) {
} }
} }
func TestConfigLogLevel(t *testing.T) {
markTestForSharding(t, 16)
defer testutils.BackupAndRestore(t)()
tester := zshTester{}
installHishtory(t, tester, "")
// Check default log level
out := tester.RunInteractiveShell(t, `hishtory config-get log-level`)
require.Equal(t, "info\n", out)
// Set log level to debug
tester.RunInteractiveShell(t, `hishtory config-set log-level debug`)
// Verify log level was changed
out = tester.RunInteractiveShell(t, `hishtory config-get log-level`)
require.Equal(t, "debug\n", out)
// Set back to default
tester.RunInteractiveShell(t, `hishtory config-set log-level info`)
// Verify log level was changed back
out = tester.RunInteractiveShell(t, `hishtory config-get log-level`)
require.Equal(t, "info\n", out)
}
// TODO: somehow test/confirm that hishtory works even if only bash/only zsh is installed // TODO: somehow test/confirm that hishtory works even if only bash/only zsh is installed

View File

@ -183,4 +183,15 @@ func init() {
configGetCmd.AddCommand(getDefaultFilterCmd) configGetCmd.AddCommand(getDefaultFilterCmd)
configGetCmd.AddCommand(getAiCompletionEndpoint) configGetCmd.AddCommand(getAiCompletionEndpoint)
configGetCmd.AddCommand(getCompactMode) configGetCmd.AddCommand(getCompactMode)
configGetCmd.AddCommand(getLogLevelCmd)
}
var getLogLevelCmd = &cobra.Command{
Use: "log-level",
Short: "Get the current log level for hishtory logs",
Run: func(cmd *cobra.Command, args []string) {
ctx := hctx.MakeContext()
config := hctx.GetConf(ctx)
fmt.Println(config.LogLevel.String())
},
} }

View File

@ -9,6 +9,7 @@ import (
"github.com/ddworken/hishtory/client/hctx" "github.com/ddworken/hishtory/client/hctx"
"github.com/ddworken/hishtory/client/lib" "github.com/ddworken/hishtory/client/lib"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -244,6 +245,23 @@ var setAiCompletionEndpoint = &cobra.Command{
}, },
} }
var setLogLevelCmd = &cobra.Command{
Use: "log-level",
Short: "Set the log level for hishtory logs",
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
ValidArgs: []string{"error", "warn", "info", "debug"},
Run: func(cmd *cobra.Command, args []string) {
level, err := logrus.ParseLevel(args[0])
if err != nil {
lib.CheckFatalError(fmt.Errorf("invalid log level: %v", err))
}
ctx := hctx.MakeContext()
config := hctx.GetConf(ctx)
config.LogLevel = level
lib.CheckFatalError(hctx.SetConfig(config))
},
}
func init() { func init() {
rootCmd.AddCommand(configSetCmd) rootCmd.AddCommand(configSetCmd)
configSetCmd.AddCommand(setEnableControlRCmd) configSetCmd.AddCommand(setEnableControlRCmd)
@ -258,6 +276,7 @@ func init() {
configSetCmd.AddCommand(setDefaultFilterCommand) configSetCmd.AddCommand(setDefaultFilterCommand)
configSetCmd.AddCommand(setAiCompletionEndpoint) configSetCmd.AddCommand(setAiCompletionEndpoint)
configSetCmd.AddCommand(compactMode) configSetCmd.AddCommand(compactMode)
configSetCmd.AddCommand(setLogLevelCmd)
setColorSchemeCmd.AddCommand(setColorSchemeSelectedText) setColorSchemeCmd.AddCommand(setColorSchemeSelectedText)
setColorSchemeCmd.AddCommand(setColorSchemeSelectedBackground) setColorSchemeCmd.AddCommand(setColorSchemeSelectedBackground)
setColorSchemeCmd.AddCommand(setColorSchemeBorderColor) setColorSchemeCmd.AddCommand(setColorSchemeBorderColor)

View File

@ -499,7 +499,7 @@ func tweakConfigForTests(configContents string) (string, error) {
ret += "\n" ret += "\n"
} }
if !(substitutionCount == 2 && removedCount == 2) { if !(substitutionCount == 2 && removedCount == 2) {
return "", fmt.Errorf("failed to find substitution line in configConents=%#v", configContents) return "", fmt.Errorf("failed to find substitution line in configContents=%#v", configContents)
} }
return ret, nil return ret, nil
} }

View File

@ -113,13 +113,13 @@ var updateLocalDbFromRemoteCmd = &cobra.Command{
if config.BetaMode { if config.BetaMode {
lib.CheckFatalError(err) lib.CheckFatalError(err)
} else if err != nil { } else if err != nil {
hctx.GetLogger().Infof("updateLocalDbFromRemote: Failed to RetrieveAdditionalEntriesFromRemote: %v", err) hctx.GetLogger().Warnf("updateLocalDbFromRemote: Failed to RetrieveAdditionalEntriesFromRemote: %v", err)
} }
err = lib.ProcessDeletionRequests(ctx) err = lib.ProcessDeletionRequests(ctx)
if config.BetaMode { if config.BetaMode {
lib.CheckFatalError(err) lib.CheckFatalError(err)
} else if err != nil { } else if err != nil {
hctx.GetLogger().Infof("updateLocalDbFromRemote: Failed to ProcessDeletionRequests: %v", err) hctx.GetLogger().Warnf("updateLocalDbFromRemote: Failed to ProcessDeletionRequests: %v", err)
} }
} }
}, },

View File

@ -124,7 +124,7 @@ func maybeUploadSkippedHistoryEntries(ctx context.Context) error {
func handlePotentialUploadFailure(ctx context.Context, err error, config *hctx.ClientConfig, entryTimestamp time.Time) { func handlePotentialUploadFailure(ctx context.Context, err error, config *hctx.ClientConfig, entryTimestamp time.Time) {
if err != nil { if err != nil {
if lib.IsOfflineError(ctx, err) { if lib.IsOfflineError(ctx, err) {
hctx.GetLogger().Infof("Failed to remotely persist hishtory entry because we failed to connect to the remote server! This is likely because the device is offline, but also could be because the remote server is having reliability issues. Original error: %v", err) hctx.GetLogger().Warnf("Failed to remotely persist hishtory entry because we failed to connect to the remote server! This is likely because the device is offline, but also could be because the remote server is having reliability issues. Original error: %v", err)
if !config.HaveMissedUploads { if !config.HaveMissedUploads {
config.HaveMissedUploads = true config.HaveMissedUploads = true
// Set MissedUploadTimestamp to `entry timestamp - 1` so that the current entry will get // Set MissedUploadTimestamp to `entry timestamp - 1` so that the current entry will get
@ -261,7 +261,7 @@ func deletePresavedEntries(ctx context.Context, entry *data.HistoryEntry, isRetr
// this function after a short delay. If it still is empty, then we assume we are in case #1. // this function after a short delay. If it still is empty, then we assume we are in case #1.
if isRetry { if isRetry {
// Already retried, assume we're in case #1 // Already retried, assume we're in case #1
hctx.GetLogger().Infof("failed to find presaved entry matching cmd=%#v even with retry, skipping delete", entry.Command) hctx.GetLogger().Warnf("failed to find presaved entry matching cmd=%#v even with retry, skipping delete", entry.Command)
return nil return nil
} else { } else {
time.Sleep(500 * time.Millisecond) time.Sleep(500 * time.Millisecond)

View File

@ -121,6 +121,9 @@ func update(ctx context.Context) error {
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) 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", lib.Version, getPossiblyOverriddenVersion(downloadData)) fmt.Printf("Successfully updated hishtory from v0.%s to %s\n", lib.Version, getPossiblyOverriddenVersion(downloadData))
// Delete the file after installing to prevent issues like #227
_ = os.Remove(getTmpClientPath())
return nil return nil
} }

View File

@ -52,8 +52,14 @@ func GetLogger() *logrus.Logger {
hishtoryLogger = logrus.New() hishtoryLogger = logrus.New()
hishtoryLogger.SetFormatter(logFormatter) hishtoryLogger.SetFormatter(logFormatter)
hishtoryLogger.SetLevel(logrus.InfoLevel)
hishtoryLogger.SetOutput(lumberjackLogger) hishtoryLogger.SetOutput(lumberjackLogger)
// Configure the log level from the config file, if the config file exists
hishtoryLogger.SetLevel(logrus.InfoLevel)
cfg, err := GetConfig()
if err == nil {
hishtoryLogger.SetLevel(cfg.LogLevel)
}
}) })
return hishtoryLogger return hishtoryLogger
} }
@ -213,6 +219,8 @@ type ClientConfig struct {
AiCompletionEndpoint string `json:"ai_completion_endpoint"` AiCompletionEndpoint string `json:"ai_completion_endpoint"`
// Custom key bindings for the TUI // Custom key bindings for the TUI
KeyBindings keybindings.SerializableKeyMap `json:"key_bindings"` KeyBindings keybindings.SerializableKeyMap `json:"key_bindings"`
// The log level for hishtory (e.g., "debug", "info", "warn", "error")
LogLevel logrus.Level `json:"log_level"`
} }
type ColorScheme struct { type ColorScheme struct {
@ -284,6 +292,9 @@ func GetConfig() (ClientConfig, error) {
if config.AiCompletionEndpoint == "" { if config.AiCompletionEndpoint == "" {
config.AiCompletionEndpoint = "https://api.openai.com/v1/chat/completions" config.AiCompletionEndpoint = "https://api.openai.com/v1/chat/completions"
} }
if config.LogLevel == logrus.Level(0) {
config.LogLevel = logrus.InfoLevel
}
return config, nil return config, nil
} }

View File

@ -55,7 +55,7 @@ func VerifyBinary(ctx context.Context, binaryPath, attestationPath, versionTag s
} }
resp, err := ApiGet(ctx, "/api/v1/slsa-status?newVersion="+versionTag) resp, err := ApiGet(ctx, "/api/v1/slsa-status?newVersion="+versionTag)
if err != nil { if err != nil {
hctx.GetLogger().Infof("Failed to query SLSA status (err=%#v), assuming that SLSA is currently working", err) hctx.GetLogger().Warnf("Failed to query SLSA status (err=%#v), assuming that SLSA is currently working", err)
} }
if err == nil && string(resp) != "OK" { if err == nil && string(resp) != "OK" {
slsa_status_error: slsa_status_error:

View File

@ -146,7 +146,7 @@ func initialModel(ctx context.Context, shellName, initialQuery string) model {
if err == nil { if err == nil {
queryInput.Width = width queryInput.Width = width
} else { } else {
hctx.GetLogger().Infof("getTerminalSize() return err=%#v, defaulting queryInput to a width of 50", err) hctx.GetLogger().Warnf("getTerminalSize() return err=%#v, defaulting queryInput to a width of 50", err)
queryInput.Width = 50 queryInput.Width = 50
} }
if initialQuery != "" { if initialQuery != "" {
@ -378,7 +378,7 @@ func (m model) View() string {
if strings.HasPrefix(changeDir, "~/") { if strings.HasPrefix(changeDir, "~/") {
homedir, err := os.UserHomeDir() homedir, err := os.UserHomeDir()
if err != nil { if err != nil {
hctx.GetLogger().Infof("UserHomeDir() return err=%v, skipping replacing ~/", err) hctx.GetLogger().Warnf("UserHomeDir() return err=%v, skipping replacing ~/", err)
} else { } else {
strippedChangeDir, _ := strings.CutPrefix(changeDir, "~/") strippedChangeDir, _ := strings.CutPrefix(changeDir, "~/")
changeDir = filepath.Join(homedir, strippedChangeDir) changeDir = filepath.Join(homedir, strippedChangeDir)
@ -425,7 +425,7 @@ func isExtraCompactHeightMode(ctx context.Context) bool {
} }
_, height, err := getTerminalSize() _, height, err := getTerminalSize()
if err != nil { if err != nil {
hctx.GetLogger().Infof("got err=%v when retrieving terminal dimensions, assuming the terminal is reasonably tall", err) hctx.GetLogger().Warnf("got err=%v when retrieving terminal dimensions, assuming the terminal is reasonably tall", err)
return false return false
} }
return height < 15 return height < 15
@ -437,7 +437,7 @@ func isCompactHeightMode(ctx context.Context) bool {
} }
_, height, err := getTerminalSize() _, height, err := getTerminalSize()
if err != nil { if err != nil {
hctx.GetLogger().Infof("got err=%v when retrieving terminal dimensions, assuming the terminal is reasonably tall", err) hctx.GetLogger().Warnf("got err=%v when retrieving terminal dimensions, assuming the terminal is reasonably tall", err)
return false return false
} }
return height < 25 return height < 25
@ -467,7 +467,7 @@ func renderNullableTable(m model, helpText string) string {
func getRowsFromAiSuggestions(ctx context.Context, columnNames []string, shellName, query string) ([]table.Row, []*data.HistoryEntry, error) { func getRowsFromAiSuggestions(ctx context.Context, columnNames []string, shellName, query string) ([]table.Row, []*data.HistoryEntry, error) {
suggestions, err := ai.DebouncedGetAiSuggestions(ctx, shellName, strings.TrimPrefix(query, "?"), 5) suggestions, err := ai.DebouncedGetAiSuggestions(ctx, shellName, strings.TrimPrefix(query, "?"), 5)
if err != nil { if err != nil {
hctx.GetLogger().Infof("failed to get AI query suggestions: %v", err) hctx.GetLogger().Warnf("failed to get AI query suggestions: %v", err)
return nil, nil, fmt.Errorf("failed to get AI query suggestions: %w", err) return nil, nil, fmt.Errorf("failed to get AI query suggestions: %w", err)
} }
var rows []table.Row var rows []table.Row
@ -716,7 +716,7 @@ func makeTable(ctx context.Context, shellName string, rows []table.Row) (table.M
if err != nil { if err != nil {
// Failed to compile the regex for highlighting matches, this should never happen. In this // Failed to compile the regex for highlighting matches, this should never happen. In this
// case, just use a regexp that matches nothing to ensure that the TUI doesn't crash. // case, just use a regexp that matches nothing to ensure that the TUI doesn't crash.
hctx.GetLogger().Infof("Failed to compile regex %#v for query %#v, disabling highlighting of matches", queryRegex, CURRENT_QUERY_FOR_HIGHLIGHTING) hctx.GetLogger().Warnf("Failed to compile regex %#v for query %#v, disabling highlighting of matches", queryRegex, CURRENT_QUERY_FOR_HIGHLIGHTING)
re = MATCH_NOTHING_REGEXP re = MATCH_NOTHING_REGEXP
} else { } else {
re = r re = r

15
go.mod
View File

@ -18,6 +18,7 @@ require (
github.com/mattn/go-runewidth v0.0.15 github.com/mattn/go-runewidth v0.0.15
github.com/muesli/termenv v0.15.2 github.com/muesli/termenv v0.15.2
github.com/rodaine/table v1.0.1 github.com/rodaine/table v1.0.1
github.com/samber/lo v1.46.0
github.com/schollz/progressbar/v3 v3.13.1 github.com/schollz/progressbar/v3 v3.13.1
github.com/sirupsen/logrus v1.9.1 github.com/sirupsen/logrus v1.9.1
github.com/slsa-framework/slsa-verifier v1.4.2-0.20221130213533-128324f48837 github.com/slsa-framework/slsa-verifier v1.4.2-0.20221130213533-128324f48837
@ -25,7 +26,7 @@ require (
github.com/stretchr/testify v1.8.4 github.com/stretchr/testify v1.8.4
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 golang.org/x/exp v0.0.0-20230321023759-10a507213a29
golang.org/x/sys v0.20.0 golang.org/x/sys v0.20.0
golang.org/x/term v0.18.0 golang.org/x/term v0.20.0
gopkg.in/DataDog/dd-trace-go.v1 v1.43.1 gopkg.in/DataDog/dd-trace-go.v1 v1.43.1
gopkg.in/natefinch/lumberjack.v2 v2.0.0 gopkg.in/natefinch/lumberjack.v2 v2.0.0
gorm.io/driver/postgres v1.3.1 gorm.io/driver/postgres v1.3.1
@ -222,14 +223,14 @@ require (
go.uber.org/zap v1.24.0 // indirect go.uber.org/zap v1.24.0 // indirect
go4.org/intern v0.0.0-20211027215823-ae77deb06f29 // indirect go4.org/intern v0.0.0-20211027215823-ae77deb06f29 // indirect
go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 // indirect go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 // indirect
golang.org/x/crypto v0.21.0 // indirect golang.org/x/crypto v0.23.0 // indirect
golang.org/x/mod v0.16.0 // indirect golang.org/x/mod v0.17.0 // indirect
golang.org/x/net v0.23.0 // indirect golang.org/x/net v0.25.0 // indirect
golang.org/x/oauth2 v0.17.0 // indirect golang.org/x/oauth2 v0.17.0 // indirect
golang.org/x/sync v0.6.0 // indirect golang.org/x/sync v0.7.0 // indirect
golang.org/x/text v0.14.0 // indirect golang.org/x/text v0.16.0 // indirect
golang.org/x/time v0.5.0 // indirect golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.19.0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/appengine v1.6.8 // indirect google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto v0.0.0-20240205150955-31a09d347014 // indirect google.golang.org/genproto v0.0.0-20240205150955-31a09d347014 // indirect

29
go.sum
View File

@ -887,6 +887,8 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
github.com/samber/lo v1.46.0 h1:w8G+oaCPgz1PoCJztqymCFaKwXt+5cCXn51uPxExFfQ=
github.com/samber/lo v1.46.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU=
github.com/sassoftware/relic v7.2.1+incompatible h1:Pwyh1F3I0r4clFJXkSI8bOyJINGqpgjJU3DYAZeI05A= github.com/sassoftware/relic v7.2.1+incompatible h1:Pwyh1F3I0r4clFJXkSI8bOyJINGqpgjJU3DYAZeI05A=
github.com/sassoftware/relic v7.2.1+incompatible/go.mod h1:CWfAxv73/iLZ17rbyhIEq3K9hs5w6FpNMdUT//qR+zk= github.com/sassoftware/relic v7.2.1+incompatible/go.mod h1:CWfAxv73/iLZ17rbyhIEq3K9hs5w6FpNMdUT//qR+zk=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
@ -1100,8 +1102,8 @@ golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -1142,8 +1144,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -1198,8 +1200,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -1229,8 +1231,8 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -1324,8 +1326,8 @@ golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -1339,8 +1341,9 @@ golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -1418,8 +1421,8 @@ golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8= golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8=
golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=