diff --git a/.goreleaser.yml b/.goreleaser.yml index 125b906..c2e2bf5 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -93,6 +93,15 @@ builds: - amd64 - arm - arm64 + - + id: "config-setup" + main: ./cmd/config-setup + binary: bin/resh-config-setup + goarch: + - 386 + - amd64 + - arm + - arm64 # signs: # - artifacts: checksum diff --git a/Makefile b/Makefile index b269c6d..9a30621 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ GOFLAGS=-ldflags "-X main.version=${VERSION} -X main.commit=${COMMIT} -X main.de build: submodules bin/resh-session-init bin/resh-collect bin/resh-postcollect bin/resh-daemon\ - bin/resh-control bin/resh-config bin/resh-cli + bin/resh-control bin/resh-config bin/resh-cli bin/resh-config-setup install: build scripts/install.sh diff --git a/cmd/collect/main.go b/cmd/collect/main.go index a0a17b2..aac282c 100644 --- a/cmd/collect/main.go +++ b/cmd/collect/main.go @@ -207,7 +207,7 @@ func main() { GitDir: gitDir, GitRealDir: gitRealDir, GitOriginRemote: *gitRemote, - MachineID: collect.ReadFileContent(machineIDPath), + MachineID: collect.ReadFileContent(out.Logger, machineIDPath), OsReleaseID: *osReleaseID, OsReleaseVersionID: *osReleaseVersionID, @@ -217,7 +217,7 @@ func main() { PartOne: true, - ReshUUID: collect.ReadFileContent(reshUUIDPath), + ReshUUID: collect.ReadFileContent(out.Logger, reshUUIDPath), ReshVersion: version, ReshRevision: commit, }, diff --git a/cmd/config-setup/main.go b/cmd/config-setup/main.go new file mode 100644 index 0000000..a1ed942 --- /dev/null +++ b/cmd/config-setup/main.go @@ -0,0 +1,46 @@ +package main + +import ( + "fmt" + "os" + + "github.com/curusarn/resh/internal/cfg" + "github.com/curusarn/resh/internal/logger" + "go.uber.org/zap" +) + +// info passed during build +var version string +var commit string +var developement bool + +func main() { + errDo := doConfigSetup() + config, errCfg := cfg.New() + logger, _ := logger.New("config-setup", config.LogLevel, developement) + defer logger.Sync() // flushes buffer, if any + + if errDo != nil { + logger.Error("Config setup failed", zap.Error(errDo)) + // TODO: better error message for people + fmt.Fprintf(os.Stderr, "ERROR: %v\n", errDo) + } + if errCfg != nil { + logger.Error("Error while getting configuration", zap.Error(errCfg)) + } +} + +func doConfigSetup() error { + err := cfg.Touch() + if err != nil { + return fmt.Errorf("could not touch config file: %w", err) + } + changes, err := cfg.Migrate() + if err != nil { + return fmt.Errorf("could not migrate config file version: %v", err) + } + if changes { + fmt.Printf("Config file format has changed - your config was updated to reflect the changes.\n") + } + return nil +} diff --git a/cmd/postcollect/main.go b/cmd/postcollect/main.go index 87f25b7..4d4fca8 100644 --- a/cmd/postcollect/main.go +++ b/cmd/postcollect/main.go @@ -145,11 +145,11 @@ func main() { GitDirAfter: gitDirAfter, GitRealDirAfter: gitRealDirAfter, GitOriginRemoteAfter: *gitRemoteAfter, - MachineID: collect.ReadFileContent(machineIDPath), + MachineID: collect.ReadFileContent(out.Logger, machineIDPath), PartOne: false, - ReshUUID: collect.ReadFileContent(reshUUIDPath), + ReshUUID: collect.ReadFileContent(out.Logger, reshUUIDPath), ReshVersion: version, ReshRevision: commit, }, diff --git a/cmd/session-init/main.go b/cmd/session-init/main.go index 0205dec..59010f7 100644 --- a/cmd/session-init/main.go +++ b/cmd/session-init/main.go @@ -174,7 +174,7 @@ func main() { RealtimeSinceSessionStart: realtimeSinceSessionStart, RealtimeSinceBoot: realtimeSinceBoot, - MachineID: collect.ReadFileContent(machineIDPath), + MachineID: collect.ReadFileContent(out.Logger, machineIDPath), OsReleaseID: *osReleaseID, OsReleaseVersionID: *osReleaseVersionID, @@ -182,7 +182,7 @@ func main() { OsReleaseName: *osReleaseName, OsReleasePrettyName: *osReleasePrettyName, - ReshUUID: collect.ReadFileContent(reshUUIDPath), + ReshUUID: collect.ReadFileContent(out.Logger, reshUUIDPath), ReshVersion: version, ReshRevision: commit, }, diff --git a/internal/cfg/cfg.go b/internal/cfg/cfg.go index 4587bf1..9c3ae7a 100644 --- a/internal/cfg/cfg.go +++ b/internal/cfg/cfg.go @@ -12,38 +12,55 @@ import ( // configFile used to parse the config file type configFile struct { + // ConfigVersion - never remove this + ConfigVersion *string + + // added in legacy Port *int SesswatchPeriodSeconds *uint SesshistInitHistorySize *int + BindControlR *bool + Debug *bool + // added in v1 LogLevel *string - BindControlR *bool - - // deprecated + // added in legacy + // deprecated in v1 BindArrowKeysBash *bool BindArrowKeysZsh *bool - Debug *bool } // Config returned by this package to be used in the rest of the project type Config struct { - Port int - SesswatchPeriodSeconds uint + // Port used by daemon and rest of the components to communicate + // Make sure to restart the daemon when you change it + Port int + + // BindControlR causes CTRL+R to launch the search app + BindControlR bool + // LogLevel used to filter logs + LogLevel zapcore.Level + + // Debug mode for search app + Debug bool + // SesswatchPeriodSeconds is how often should daemon check if terminal + // sessions are still alive + SesswatchPeriodSeconds uint + // SesshistInitHistorySize is how large resh history needs to be for + // daemon to ignore standard shell history files SesshistInitHistorySize int - LogLevel zapcore.Level - Debug bool - BindControlR bool } // defaults for config var defaults = Config{ - Port: 2627, + Port: 2627, + LogLevel: zap.InfoLevel, + BindControlR: true, + + Debug: false, SesswatchPeriodSeconds: 120, SesshistInitHistorySize: 1000, - LogLevel: zap.InfoLevel, - Debug: false, - BindControlR: true, } func getConfigPath() (string, error) { @@ -59,18 +76,22 @@ func getConfigPath() (string, error) { return path.Join(homeDir, ".config", fname), nil } -func readConfig() (*configFile, error) { +func readConfig(path string) (*configFile, error) { var config configFile - path, err := getConfigPath() - if err != nil { - return &config, fmt.Errorf("could not get config file path: %w", err) - } if _, err := toml.DecodeFile(path, &config); err != nil { return &config, fmt.Errorf("could not decode config: %w", err) } return &config, nil } +func getConfig() (*configFile, error) { + path, err := getConfigPath() + if err != nil { + return nil, fmt.Errorf("could not get config file path: %w", err) + } + return readConfig(path) +} + func processAndFillDefaults(configF *configFile) (Config, error) { config := defaults @@ -104,9 +125,9 @@ func processAndFillDefaults(configF *configFile) (Config, error) { // New returns a config file // returned config is always usable, returned errors are informative func New() (Config, error) { - configF, err := readConfig() + configF, err := getConfig() if err != nil { - return defaults, fmt.Errorf("using default config because of error while getting config: %w", err) + return defaults, fmt.Errorf("using default config because of error while getting/reading config: %w", err) } config, err := processAndFillDefaults(configF) diff --git a/internal/cfg/migrate.go b/internal/cfg/migrate.go new file mode 100644 index 0000000..4f91278 --- /dev/null +++ b/internal/cfg/migrate.go @@ -0,0 +1,110 @@ +package cfg + +import ( + "fmt" + "os" + + "github.com/BurntSushi/toml" +) + +// Touch config file +func Touch() error { + path, err := getConfigPath() + if err != nil { + return fmt.Errorf("could not get config file path: %w", err) + } + file, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666) + if err != nil { + return fmt.Errorf("could not open/create config file: %w", err) + } + err = file.Close() + if err != nil { + return fmt.Errorf("could not close config file: %w", err) + } + return nil +} + +// Migrate old config versions to current config version +// returns true if any changes were made to the config +func Migrate() (bool, error) { + path, err := getConfigPath() + if err != nil { + return false, fmt.Errorf("could not get config file path: %w", err) + } + configF, err := readConfig(path) + if err != nil { + return false, fmt.Errorf("could not read config: %w", err) + } + const current = "v1" + if configF.ConfigVersion != nil && *configF.ConfigVersion == current { + return false, nil + } + + if configF.ConfigVersion == nil { + configF, err = legacyToV1(configF) + if err != nil { + return true, fmt.Errorf("error converting config from version 'legacy' to 'v1': %w", err) + } + } + + if *configF.ConfigVersion != current { + return false, fmt.Errorf("unrecognized config version: '%s'", *configF.ConfigVersion) + } + err = writeConfig(configF, path) + if err != nil { + return true, fmt.Errorf("could not write migrated config: %w", err) + } + return true, nil +} + +func writeConfig(config *configFile, path string) error { + file, err := os.OpenFile(path, os.O_RDWR|os.O_TRUNC, 0666) + if err != nil { + return fmt.Errorf("could not open config for writing: %w", err) + } + defer file.Close() + err = toml.NewEncoder(file).Encode(config) + if err != nil { + return fmt.Errorf("could not encode config: %w", err) + } + return nil +} + +func legacyToV1(config *configFile) (*configFile, error) { + if config.ConfigVersion != nil { + return nil, fmt.Errorf("config version is not 'legacy': '%s'", *config.ConfigVersion) + } + version := "v1" + newConf := configFile{ + ConfigVersion: &version, + } + // Remove defaults + if config.Port != nil && *config.Port != 2627 { + newConf.Port = config.Port + } + if config.SesswatchPeriodSeconds != nil && *config.SesswatchPeriodSeconds != 120 { + newConf.SesswatchPeriodSeconds = config.SesswatchPeriodSeconds + } + if config.SesshistInitHistorySize != nil && *config.SesshistInitHistorySize != 1000 { + newConf.SesshistInitHistorySize = config.SesshistInitHistorySize + } + if config.BindControlR != nil && *config.BindControlR != true { + newConf.BindControlR = config.BindControlR + } + if config.Debug != nil && *config.Debug != false { + newConf.Debug = config.Debug + } + return &newConf, nil +} + +// func v1ToV2(config *configFile) (*configFile, error) { +// if *config.ConfigVersion != "v1" { +// return nil, fmt.Errorf("config version is not 'legacy': '%s'", *config.ConfigVersion) +// } +// version := "v2" +// newConf := configFile{ +// ConfigVersion: &version, +// // Here goes all config fields - no need to prune defaults like we do for legacy +// } +// return &newConf, nil +// } diff --git a/internal/collect/collect.go b/internal/collect/collect.go index a72825c..e9fc585 100644 --- a/internal/collect/collect.go +++ b/internal/collect/collect.go @@ -15,12 +15,6 @@ import ( "go.uber.org/zap" ) -// SingleResponse json struct -type SingleResponse struct { - Found bool `json:"found"` - CmdLine string `json:"cmdline"` -} - // SendRecord to daemon func SendRecord(out *output.Output, r records.Record, port, path string) { out.Logger.Debug("Sending record ...", @@ -49,11 +43,14 @@ func SendRecord(out *output.Output, r records.Record, port, path string) { } // ReadFileContent and return it as a string -func ReadFileContent(path string) string { +func ReadFileContent(logger *zap.Logger, path string) string { dat, err := ioutil.ReadFile(path) if err != nil { + logger.Error("Error reading file", + zap.String("filePath", path), + zap.Error(err), + ) return "" - //sugar.Fatal("failed to open " + path) } return strings.TrimSuffix(string(dat), "\n") } @@ -84,7 +81,7 @@ func GetTimezoneOffsetInSeconds(logger *zap.Logger, zone string) float64 { } mins, err := strconv.Atoi(minsStr) if err != nil { - logger.Error("err while parsing minutes in timezone offset:", zap.Error(err)) + logger.Error("Errot while parsing minutes in timezone offset:", zap.Error(err)) return -1 } secs := ((hours * 60) + mins) * 60 diff --git a/scripts/install.sh b/scripts/install.sh index 5caedca..b07ac4d 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -116,42 +116,23 @@ cp -f scripts/shellrc.sh ~/.resh/shellrc cp -f scripts/reshctl.sh scripts/widgets.sh scripts/hooks.sh scripts/util.sh ~/.resh/ cp -f scripts/rawinstall.sh ~/.resh/ -update_config() { - version=$1 - key=$2 - value=$3 - # TODO: create bin/semver-lt - if bin/semver-lt "${__RESH_VERSION:-0.0.0}" "$1" && [ "$(bin/resh-config -key $key)" != "$value" ] ; then - echo " * config option $key was updated to $value" - # TODO: enable resh-config value setting - # resh-config -key "$key" -value "$value" - fi -} - - -# Do not overwrite config if it exists -if [ ! -f ~/.config/resh.toml ]; then - echo "Copying config file ..." - cp -f conf/config.toml ~/.config/resh.toml -# else - # echo "Merging config files ..." - # NOTE: This is where we will merge configs when we make changes to the upstream config - # HINT: check which version are we updating FROM and make changes to config based on that -fi - echo "Generating completions ..." bin/resh-control completion bash > ~/.resh/bash_completion.d/_reshctl bin/resh-control completion zsh > ~/.resh/zsh_completion.d/_reshctl echo "Copying more files ..." cp -f scripts/uuid.sh ~/.resh/bin/resh-uuid -cp -f bin/* ~/.resh/bin/ +rm ~/.resh/bin/resh-* ||: +cp -f bin/resh-{daemon,control,collect,postcollect,session-init,config} ~/.resh/bin/ cp -f scripts/resh-evaluate-plot.py ~/.resh/bin/ cp -fr data/sanitizer ~/.resh/sanitizer_data # backward compatibility: We have a new location for resh history file [ ! -f ~/.resh/history.json ] || mv ~/.resh/history.json ~/.resh_history.json +echo "Checking config file ..." +./bin/resh-config-setup + echo "Finishing up ..." # Adding resh shellrc to .bashrc ... if [ ! -f ~/.bashrc ]; then