CFG_VER/APP_VER in the env file: invalid configs replaced, updates follow APP_VER (dev/stable/pin)
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
// Shared env-file handling for the installers (Windows and Linux).
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"gpu-turnstile/internal/config"
|
||||
)
|
||||
|
||||
// syncEnvFile ensures the installed env file at path is a current
|
||||
// installer-written sample. A missing file is created; a file without a
|
||||
// CFG_VER line (hand-written or from before versioning) is invalid and
|
||||
// replaced by a fresh sample after a .bak backup; a file written by an
|
||||
// older version gets newly added settings appended via config.SyncSample.
|
||||
// logPath activates the LOG_FILE line (Windows); empty leaves it commented.
|
||||
func syncEnvFile(path, version, logPath string) error {
|
||||
data, err := os.ReadFile(path)
|
||||
switch {
|
||||
case os.IsNotExist(err):
|
||||
return writeEnvFile(path, config.SampleEnv(version, logPath))
|
||||
case err != nil:
|
||||
return fmt.Errorf("read %s: %w", path, err)
|
||||
}
|
||||
values, perr := config.ParseEnvFile(strings.NewReader(string(data)))
|
||||
if perr == nil && values["CFG_VER"] != "" {
|
||||
synced, changed := config.SyncSample(string(data), version, logPath)
|
||||
if !changed {
|
||||
return nil
|
||||
}
|
||||
return writeEnvFile(path, synced)
|
||||
}
|
||||
// No readable CFG_VER: the file is invalid — keep a backup and start
|
||||
// from a fresh sample.
|
||||
if err := os.WriteFile(path+".bak", data, 0o644); err != nil {
|
||||
return fmt.Errorf("back up %s: %w", path, err)
|
||||
}
|
||||
return writeEnvFile(path, config.SampleEnv(version, logPath))
|
||||
}
|
||||
|
||||
func writeEnvFile(path, content string) error {
|
||||
if err := os.WriteFile(path, []byte(content), 0o644); err != nil {
|
||||
return fmt.Errorf("write %s: %w", path, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -15,8 +15,6 @@ import (
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
"gpu-turnstile/internal/config"
|
||||
)
|
||||
|
||||
// Name matches the Windows service name; the systemd unit is Name + ".service".
|
||||
@@ -172,26 +170,16 @@ func Install(configPath string, copyBin bool, version string) error {
|
||||
}
|
||||
}
|
||||
exe = installedExe
|
||||
data, readErr := os.ReadFile(etcConfig)
|
||||
switch {
|
||||
case os.IsNotExist(readErr):
|
||||
if configPath != "" {
|
||||
copyFile(configPath, etcConfig, 0o644) //nolint:errcheck // best effort
|
||||
} else if err := os.WriteFile(etcConfig, []byte(config.SampleEnv(version, "")), 0o644); err != nil {
|
||||
// Fully commented sample covering every setting; LOG_FILE
|
||||
// stays commented — stderr goes to the journal on Linux.
|
||||
return fmt.Errorf("write %s: %w", etcConfig, err)
|
||||
}
|
||||
case readErr != nil:
|
||||
return fmt.Errorf("read %s: %w", etcConfig, readErr)
|
||||
default:
|
||||
// An installer-written config from an older version gets any
|
||||
// new settings appended; hand-written files stay untouched.
|
||||
if synced, changed := config.SyncSample(string(data), version, ""); changed {
|
||||
if err := os.WriteFile(etcConfig, []byte(synced), 0o644); err != nil {
|
||||
return fmt.Errorf("write %s: %w", etcConfig, err)
|
||||
}
|
||||
}
|
||||
if _, err := os.Stat(etcConfig); os.IsNotExist(err) && configPath != "" {
|
||||
copyFile(configPath, etcConfig, 0o644) //nolint:errcheck // best effort
|
||||
}
|
||||
// Missing configs get a fully commented sample (LOG_FILE stays
|
||||
// commented — stderr goes to the journal on Linux);
|
||||
// installer-written ones from older versions get new settings
|
||||
// appended; anything without CFG_VER is invalid and gets replaced
|
||||
// (backup kept as .bak).
|
||||
if err := syncEnvFile(etcConfig, version, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if configPath != "" {
|
||||
if abs, absErr := filepath.Abs(configPath); absErr == nil {
|
||||
|
||||
@@ -172,28 +172,14 @@ func Install(configPath string, copyBin bool, version string) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
// A service has no console: without LOG_FILE the output vanishes.
|
||||
// With no config at all, install a fully commented sample covering
|
||||
// every setting (only LOG_FILE active). An existing installer-written
|
||||
// config from an older version gets any new settings appended; any
|
||||
// other existing config just gets a LOG_FILE line if it has none.
|
||||
// A service has no console: without LOG_FILE the output vanishes, so
|
||||
// the sample pre-wires it to ProgramData. Missing configs get a
|
||||
// fresh sample; installer-written ones from older versions get new
|
||||
// settings appended; anything without CFG_VER is invalid and gets
|
||||
// replaced (backup kept as .bak).
|
||||
logPath := filepath.Join(dataDir, "gpu-turnstile.log")
|
||||
data, readErr := os.ReadFile(targetCfg)
|
||||
switch {
|
||||
case os.IsNotExist(readErr):
|
||||
if err := os.WriteFile(targetCfg, []byte(config.SampleEnv(version, logPath)), 0o644); err != nil {
|
||||
return fmt.Errorf("write %s: %w", targetCfg, err)
|
||||
}
|
||||
case readErr != nil:
|
||||
return fmt.Errorf("read %s: %w", targetCfg, readErr)
|
||||
default:
|
||||
if synced, changed := config.SyncSample(string(data), version, logPath); changed {
|
||||
if err := os.WriteFile(targetCfg, []byte(synced), 0o644); err != nil {
|
||||
return fmt.Errorf("write %s: %w", targetCfg, err)
|
||||
}
|
||||
} else if err := ensureLogFile(targetCfg, logPath); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := syncEnvFile(targetCfg, version, logPath); err != nil {
|
||||
return err
|
||||
}
|
||||
configPath = targetCfg
|
||||
}
|
||||
@@ -329,30 +315,6 @@ func sameFileContent(a, b string) (bool, error) {
|
||||
return bytes.Equal(ba, bb), nil
|
||||
}
|
||||
|
||||
// ensureLogFile makes sure the env file at path sets LOG_FILE, creating
|
||||
// the file or appending the line as needed. An existing LOG_FILE= line
|
||||
// (even an empty one) is respected and left untouched.
|
||||
func ensureLogFile(path, logPath string) error {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("read %s: %w", path, err)
|
||||
}
|
||||
for _, line := range strings.Split(string(data), "\n") {
|
||||
if strings.HasPrefix(strings.TrimSpace(line), "LOG_FILE=") {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
s := string(data)
|
||||
if s != "" && !strings.HasSuffix(s, "\n") {
|
||||
s += "\n"
|
||||
}
|
||||
s += "LOG_FILE=" + logPath + "\n"
|
||||
if err := os.WriteFile(path, []byte(s), 0o644); err != nil {
|
||||
return fmt.Errorf("write %s: %w", path, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// copyFile copies src to dst (0755 on the new file).
|
||||
func copyFile(src, dst string) error {
|
||||
in, err := os.Open(src)
|
||||
|
||||
Reference in New Issue
Block a user