134 lines
3.0 KiB
Go
134 lines
3.0 KiB
Go
package main
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"reasonix/internal/config"
|
|
"reasonix/internal/fileutil"
|
|
)
|
|
|
|
// upsertDotEnv stores KEY=value in Reasonix's global .env and applies it to the
|
|
// running process so a rebuild picks it up without a restart.
|
|
func upsertDotEnv(key, value string) error {
|
|
_, err := config.SetCredential(key, value)
|
|
return err
|
|
}
|
|
|
|
func removeDotEnv(key string) error {
|
|
return config.RemoveCredential(key)
|
|
}
|
|
|
|
// upsertEnvFile merges KEY=value into a KEY=value file at path, preserving
|
|
// comments and unrelated lines, writing atomically via a sibling temp + rename.
|
|
func upsertEnvFile(path, key, value string) error {
|
|
key = strings.TrimSpace(key)
|
|
if key == "" {
|
|
return nil
|
|
}
|
|
var lines []string
|
|
if b, err := readFileUTF8(path); err == nil {
|
|
lines = strings.Split(strings.TrimRight(string(b), "\n"), "\n")
|
|
}
|
|
replaced := false
|
|
for i, ln := range lines {
|
|
t := strings.TrimSpace(ln)
|
|
if t == "" || strings.HasPrefix(t, "#") {
|
|
continue
|
|
}
|
|
if k, _, ok := strings.Cut(t, "="); ok && strings.TrimSpace(k) == key {
|
|
lines[i] = key + "=" + value
|
|
replaced = true
|
|
break
|
|
}
|
|
}
|
|
if !replaced {
|
|
lines = append(lines, key+"="+value)
|
|
}
|
|
out := strings.Join(lines, "\n") + "\n"
|
|
|
|
dir := filepath.Dir(path)
|
|
if dir != "" && dir != "." {
|
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
tmp, err := os.CreateTemp(dir, "credentials.*.tmp")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
tmpPath := tmp.Name()
|
|
if _, err := tmp.WriteString(out); err != nil {
|
|
tmp.Close()
|
|
os.Remove(tmpPath)
|
|
return err
|
|
}
|
|
if err := tmp.Close(); err != nil {
|
|
os.Remove(tmpPath)
|
|
return err
|
|
}
|
|
if err := fileutil.ReplaceFile(tmpPath, path); err != nil {
|
|
os.Remove(tmpPath)
|
|
return err
|
|
}
|
|
return os.Setenv(key, value)
|
|
}
|
|
|
|
func removeEnvFile(path, key string) error {
|
|
key = strings.TrimSpace(key)
|
|
if key == "" {
|
|
return nil
|
|
}
|
|
data, err := readFileUTF8(path)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return os.Unsetenv(key)
|
|
}
|
|
return err
|
|
}
|
|
lines := strings.Split(strings.TrimRight(string(data), "\n"), "\n")
|
|
outLines := make([]string, 0, len(lines))
|
|
for _, ln := range lines {
|
|
t := strings.TrimSpace(strings.TrimPrefix(strings.TrimSpace(ln), "export "))
|
|
if t == "" || strings.HasPrefix(t, "#") {
|
|
outLines = append(outLines, ln)
|
|
continue
|
|
}
|
|
if k, _, ok := strings.Cut(t, "="); ok && strings.TrimSpace(k) == key {
|
|
continue
|
|
}
|
|
outLines = append(outLines, ln)
|
|
}
|
|
out := ""
|
|
if len(outLines) > 0 {
|
|
out = strings.Join(outLines, "\n") + "\n"
|
|
}
|
|
|
|
dir := filepath.Dir(path)
|
|
if dir != "" && dir != "." {
|
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
tmp, err := os.CreateTemp(dir, "credentials.*.tmp")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
tmpPath := tmp.Name()
|
|
if _, err := tmp.WriteString(out); err != nil {
|
|
tmp.Close()
|
|
os.Remove(tmpPath)
|
|
return err
|
|
}
|
|
if err := tmp.Close(); err != nil {
|
|
os.Remove(tmpPath)
|
|
return err
|
|
}
|
|
if err := fileutil.ReplaceFile(tmpPath, path); err != nil {
|
|
os.Remove(tmpPath)
|
|
return err
|
|
}
|
|
return os.Unsetenv(key)
|
|
}
|