config migration during install

pull/184/head
Simon Let 4 years ago committed by Simon Let
parent 6fbd183b3e
commit 00c1262a9b
  1. 9
      .goreleaser.yml
  2. 2
      Makefile
  3. 4
      cmd/collect/main.go
  4. 46
      cmd/config-setup/main.go
  5. 4
      cmd/postcollect/main.go
  6. 4
      cmd/session-init/main.go
  7. 55
      internal/cfg/cfg.go
  8. 110
      internal/cfg/migrate.go
  9. 15
      internal/collect/collect.go
  10. 29
      scripts/install.sh

@ -93,6 +93,15 @@ builds:
- amd64 - amd64
- arm - arm
- arm64 - arm64
-
id: "config-setup"
main: ./cmd/config-setup
binary: bin/resh-config-setup
goarch:
- 386
- amd64
- arm
- arm64
# signs: # signs:
# - artifacts: checksum # - artifacts: checksum

@ -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\ 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 install: build
scripts/install.sh scripts/install.sh

@ -207,7 +207,7 @@ func main() {
GitDir: gitDir, GitDir: gitDir,
GitRealDir: gitRealDir, GitRealDir: gitRealDir,
GitOriginRemote: *gitRemote, GitOriginRemote: *gitRemote,
MachineID: collect.ReadFileContent(machineIDPath), MachineID: collect.ReadFileContent(out.Logger, machineIDPath),
OsReleaseID: *osReleaseID, OsReleaseID: *osReleaseID,
OsReleaseVersionID: *osReleaseVersionID, OsReleaseVersionID: *osReleaseVersionID,
@ -217,7 +217,7 @@ func main() {
PartOne: true, PartOne: true,
ReshUUID: collect.ReadFileContent(reshUUIDPath), ReshUUID: collect.ReadFileContent(out.Logger, reshUUIDPath),
ReshVersion: version, ReshVersion: version,
ReshRevision: commit, ReshRevision: commit,
}, },

@ -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
}

@ -145,11 +145,11 @@ func main() {
GitDirAfter: gitDirAfter, GitDirAfter: gitDirAfter,
GitRealDirAfter: gitRealDirAfter, GitRealDirAfter: gitRealDirAfter,
GitOriginRemoteAfter: *gitRemoteAfter, GitOriginRemoteAfter: *gitRemoteAfter,
MachineID: collect.ReadFileContent(machineIDPath), MachineID: collect.ReadFileContent(out.Logger, machineIDPath),
PartOne: false, PartOne: false,
ReshUUID: collect.ReadFileContent(reshUUIDPath), ReshUUID: collect.ReadFileContent(out.Logger, reshUUIDPath),
ReshVersion: version, ReshVersion: version,
ReshRevision: commit, ReshRevision: commit,
}, },

@ -174,7 +174,7 @@ func main() {
RealtimeSinceSessionStart: realtimeSinceSessionStart, RealtimeSinceSessionStart: realtimeSinceSessionStart,
RealtimeSinceBoot: realtimeSinceBoot, RealtimeSinceBoot: realtimeSinceBoot,
MachineID: collect.ReadFileContent(machineIDPath), MachineID: collect.ReadFileContent(out.Logger, machineIDPath),
OsReleaseID: *osReleaseID, OsReleaseID: *osReleaseID,
OsReleaseVersionID: *osReleaseVersionID, OsReleaseVersionID: *osReleaseVersionID,
@ -182,7 +182,7 @@ func main() {
OsReleaseName: *osReleaseName, OsReleaseName: *osReleaseName,
OsReleasePrettyName: *osReleasePrettyName, OsReleasePrettyName: *osReleasePrettyName,
ReshUUID: collect.ReadFileContent(reshUUIDPath), ReshUUID: collect.ReadFileContent(out.Logger, reshUUIDPath),
ReshVersion: version, ReshVersion: version,
ReshRevision: commit, ReshRevision: commit,
}, },

@ -12,38 +12,55 @@ import (
// configFile used to parse the config file // configFile used to parse the config file
type configFile struct { type configFile struct {
// ConfigVersion - never remove this
ConfigVersion *string
// added in legacy
Port *int Port *int
SesswatchPeriodSeconds *uint SesswatchPeriodSeconds *uint
SesshistInitHistorySize *int SesshistInitHistorySize *int
BindControlR *bool
Debug *bool
// added in v1
LogLevel *string LogLevel *string
BindControlR *bool // added in legacy
// deprecated in v1
// deprecated
BindArrowKeysBash *bool BindArrowKeysBash *bool
BindArrowKeysZsh *bool BindArrowKeysZsh *bool
Debug *bool
} }
// Config returned by this package to be used in the rest of the project // Config returned by this package to be used in the rest of the project
type Config struct { type Config struct {
// Port used by daemon and rest of the components to communicate
// Make sure to restart the daemon when you change it
Port int Port int
SesswatchPeriodSeconds uint
SesshistInitHistorySize int // BindControlR causes CTRL+R to launch the search app
BindControlR bool
// LogLevel used to filter logs
LogLevel zapcore.Level LogLevel zapcore.Level
// Debug mode for search app
Debug bool Debug bool
BindControlR 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
} }
// defaults for config // defaults for config
var defaults = Config{ var defaults = Config{
Port: 2627, Port: 2627,
SesswatchPeriodSeconds: 120,
SesshistInitHistorySize: 1000,
LogLevel: zap.InfoLevel, LogLevel: zap.InfoLevel,
Debug: false,
BindControlR: true, BindControlR: true,
Debug: false,
SesswatchPeriodSeconds: 120,
SesshistInitHistorySize: 1000,
} }
func getConfigPath() (string, error) { func getConfigPath() (string, error) {
@ -59,18 +76,22 @@ func getConfigPath() (string, error) {
return path.Join(homeDir, ".config", fname), nil return path.Join(homeDir, ".config", fname), nil
} }
func readConfig() (*configFile, error) { func readConfig(path string) (*configFile, error) {
var config configFile 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 { if _, err := toml.DecodeFile(path, &config); err != nil {
return &config, fmt.Errorf("could not decode config: %w", err) return &config, fmt.Errorf("could not decode config: %w", err)
} }
return &config, nil 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) { func processAndFillDefaults(configF *configFile) (Config, error) {
config := defaults config := defaults
@ -104,9 +125,9 @@ func processAndFillDefaults(configF *configFile) (Config, error) {
// New returns a config file // New returns a config file
// returned config is always usable, returned errors are informative // returned config is always usable, returned errors are informative
func New() (Config, error) { func New() (Config, error) {
configF, err := readConfig() configF, err := getConfig()
if err != nil { 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) config, err := processAndFillDefaults(configF)

@ -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
// }

@ -15,12 +15,6 @@ import (
"go.uber.org/zap" "go.uber.org/zap"
) )
// SingleResponse json struct
type SingleResponse struct {
Found bool `json:"found"`
CmdLine string `json:"cmdline"`
}
// SendRecord to daemon // SendRecord to daemon
func SendRecord(out *output.Output, r records.Record, port, path string) { func SendRecord(out *output.Output, r records.Record, port, path string) {
out.Logger.Debug("Sending record ...", 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 // 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) dat, err := ioutil.ReadFile(path)
if err != nil { if err != nil {
logger.Error("Error reading file",
zap.String("filePath", path),
zap.Error(err),
)
return "" return ""
//sugar.Fatal("failed to open " + path)
} }
return strings.TrimSuffix(string(dat), "\n") return strings.TrimSuffix(string(dat), "\n")
} }
@ -84,7 +81,7 @@ func GetTimezoneOffsetInSeconds(logger *zap.Logger, zone string) float64 {
} }
mins, err := strconv.Atoi(minsStr) mins, err := strconv.Atoi(minsStr)
if err != nil { 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 return -1
} }
secs := ((hours * 60) + mins) * 60 secs := ((hours * 60) + mins) * 60

@ -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/reshctl.sh scripts/widgets.sh scripts/hooks.sh scripts/util.sh ~/.resh/
cp -f scripts/rawinstall.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 ..." echo "Generating completions ..."
bin/resh-control completion bash > ~/.resh/bash_completion.d/_reshctl bin/resh-control completion bash > ~/.resh/bash_completion.d/_reshctl
bin/resh-control completion zsh > ~/.resh/zsh_completion.d/_reshctl bin/resh-control completion zsh > ~/.resh/zsh_completion.d/_reshctl
echo "Copying more files ..." echo "Copying more files ..."
cp -f scripts/uuid.sh ~/.resh/bin/resh-uuid 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 -f scripts/resh-evaluate-plot.py ~/.resh/bin/
cp -fr data/sanitizer ~/.resh/sanitizer_data cp -fr data/sanitizer ~/.resh/sanitizer_data
# backward compatibility: We have a new location for resh history file # backward compatibility: We have a new location for resh history file
[ ! -f ~/.resh/history.json ] || mv ~/.resh/history.json ~/.resh_history.json [ ! -f ~/.resh/history.json ] || mv ~/.resh/history.json ~/.resh_history.json
echo "Checking config file ..."
./bin/resh-config-setup
echo "Finishing up ..." echo "Finishing up ..."
# Adding resh shellrc to .bashrc ... # Adding resh shellrc to .bashrc ...
if [ ! -f ~/.bashrc ]; then if [ ! -f ~/.bashrc ]; then

Loading…
Cancel
Save