2023-07-12 20:45:29 +02:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"flag"
|
|
|
|
"log"
|
|
|
|
"os"
|
2023-07-23 16:39:02 +02:00
|
|
|
"os/exec"
|
2023-07-12 20:45:29 +02:00
|
|
|
"regexp"
|
2023-07-23 16:39:02 +02:00
|
|
|
"strings"
|
2023-07-12 20:45:29 +02:00
|
|
|
)
|
|
|
|
|
2023-07-12 20:54:17 +02:00
|
|
|
type file struct {
|
|
|
|
Path string
|
|
|
|
TargetPath string
|
|
|
|
}
|
|
|
|
|
2023-07-12 20:45:29 +02:00
|
|
|
type config struct {
|
2023-07-13 20:15:42 +02:00
|
|
|
Files []file
|
|
|
|
VariableRegex string
|
|
|
|
RegexGroup int
|
|
|
|
ForceOverwrite bool
|
2023-07-23 16:39:02 +02:00
|
|
|
|
|
|
|
UpdateUID bool
|
|
|
|
User string
|
|
|
|
UIDVariable string
|
|
|
|
|
|
|
|
UpdateGID bool
|
|
|
|
Group string
|
|
|
|
GIDVariable string
|
2023-07-23 21:34:32 +02:00
|
|
|
|
|
|
|
RunBefore [][]string
|
2023-07-12 20:45:29 +02:00
|
|
|
}
|
|
|
|
|
2023-07-23 16:39:02 +02:00
|
|
|
const defaultVariableRegex = `\$\{([a-zA-Z0-9_-]+)\}`
|
|
|
|
const defaultUIDVariable = "UID"
|
|
|
|
const defaultGIDVariable = "GID"
|
2023-07-12 20:45:29 +02:00
|
|
|
|
|
|
|
func loadConfig(path string) (*config, error) {
|
|
|
|
raw, err := os.ReadFile(path)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var config config
|
|
|
|
err = json.Unmarshal(raw, &config)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &config, nil
|
|
|
|
}
|
|
|
|
|
2023-07-12 20:54:17 +02:00
|
|
|
func filterFile(file file, regex regexp.Regexp, regexGroup int) error {
|
|
|
|
data, err := os.ReadFile(file.Path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
stat, err := os.Stat(file.Path)
|
2023-07-12 20:45:29 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
text := string(data)
|
|
|
|
|
|
|
|
text = regex.ReplaceAllStringFunc(text, func(s string) string {
|
|
|
|
matches := regex.FindStringSubmatch(text)
|
|
|
|
return os.Getenv(matches[regexGroup])
|
|
|
|
})
|
|
|
|
|
2023-07-12 20:54:17 +02:00
|
|
|
err = os.WriteFile(file.TargetPath, []byte(text), stat.Mode())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-07-12 20:45:29 +02:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-07-13 20:15:42 +02:00
|
|
|
func fileExists(path string) (bool, error) {
|
|
|
|
_, err := os.Stat(path)
|
|
|
|
if err == nil {
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
2023-07-12 20:45:29 +02:00
|
|
|
func filterFiles(config config) error {
|
|
|
|
var regexString string
|
|
|
|
if len(config.VariableRegex) == 0 {
|
|
|
|
regexString = defaultVariableRegex
|
|
|
|
} else {
|
|
|
|
regexString = config.VariableRegex
|
|
|
|
}
|
|
|
|
|
|
|
|
var regexGroup int
|
|
|
|
if config.RegexGroup == 0 {
|
|
|
|
regexGroup = 1
|
|
|
|
} else {
|
|
|
|
regexGroup = config.RegexGroup
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Println("Regex is:", regexString)
|
|
|
|
|
|
|
|
regex, err := regexp.Compile(regexString)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, file := range config.Files {
|
2023-07-13 20:15:42 +02:00
|
|
|
if !config.ForceOverwrite {
|
|
|
|
exists, err := fileExists(file.TargetPath)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if exists {
|
|
|
|
log.Println("Skipping file:", file.Path, "->", file.TargetPath, "(already exists)")
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Println("Filtering file:", file.Path, "->", file.TargetPath)
|
2023-07-12 20:45:29 +02:00
|
|
|
err := filterFile(file, *regex, regexGroup)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-07-23 16:39:02 +02:00
|
|
|
func updateUID(config config) error {
|
|
|
|
uidVariable := config.UIDVariable
|
|
|
|
if len(uidVariable) == 0 {
|
|
|
|
uidVariable = defaultUIDVariable
|
|
|
|
}
|
|
|
|
|
|
|
|
newUID := os.Getenv(uidVariable)
|
|
|
|
if len(newUID) == 0 {
|
|
|
|
log.Println("UID variable is not set. Not updating UID")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-07-23 21:34:32 +02:00
|
|
|
log.Println("Updating UID of user", config.User)
|
|
|
|
cmd := exec.Command("usermod", "-u", newUID, config.User)
|
|
|
|
cmd.Stdout = os.Stdout
|
|
|
|
cmd.Stderr = os.Stderr
|
|
|
|
err := cmd.Run()
|
2023-07-23 16:39:02 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func updateGID(config config) error {
|
|
|
|
gidVariable := config.GIDVariable
|
|
|
|
if len(gidVariable) == 0 {
|
|
|
|
gidVariable = defaultGIDVariable
|
|
|
|
}
|
|
|
|
|
|
|
|
newGID := os.Getenv(gidVariable)
|
|
|
|
if len(newGID) == 0 {
|
|
|
|
log.Println("GID variable is not set. Not updating GID")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-07-23 21:34:32 +02:00
|
|
|
log.Println("Updating GID of user", config.Group)
|
|
|
|
cmd := exec.Command("groupmod", "-g", newGID, config.Group)
|
|
|
|
cmd.Stdout = os.Stdout
|
|
|
|
cmd.Stderr = os.Stderr
|
|
|
|
err := cmd.Run()
|
2023-07-23 16:39:02 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-07-12 20:45:29 +02:00
|
|
|
func main() {
|
|
|
|
configPath := flag.String("config", "config.json", "Path to config file")
|
2023-07-13 20:15:42 +02:00
|
|
|
force := flag.Bool("force", false, "Forcibly overwrite existing files (equivalent to setting forceOverwrite to true in the config)")
|
2023-07-12 20:45:29 +02:00
|
|
|
flag.Parse()
|
|
|
|
|
2023-07-23 16:39:02 +02:00
|
|
|
if flag.NArg() == 0 {
|
|
|
|
log.Fatal("Missing command")
|
|
|
|
}
|
|
|
|
|
2023-07-12 20:45:29 +02:00
|
|
|
config, err := loadConfig(*configPath)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalln("Failed to load config:", err)
|
|
|
|
}
|
|
|
|
|
2023-07-13 20:15:42 +02:00
|
|
|
config.ForceOverwrite = config.ForceOverwrite || *force
|
|
|
|
|
2023-07-12 20:45:29 +02:00
|
|
|
err = filterFiles(*config)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalln(err)
|
|
|
|
}
|
2023-07-23 16:39:02 +02:00
|
|
|
|
|
|
|
if config.UpdateUID {
|
|
|
|
err = updateUID(*config)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalln(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if config.UpdateGID {
|
|
|
|
err = updateGID(*config)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalln(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-23 21:34:32 +02:00
|
|
|
for _, command := range config.RunBefore {
|
|
|
|
log.Println(">", strings.Join(command, " "))
|
|
|
|
cmd := exec.Command(command[0], command[1:]...)
|
|
|
|
cmd.Stdout = os.Stdout
|
|
|
|
cmd.Stderr = os.Stderr
|
|
|
|
|
|
|
|
err = cmd.Run()
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalln(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-23 16:39:02 +02:00
|
|
|
command := flag.Args()
|
|
|
|
log.Println(">", strings.Join(command, " "))
|
|
|
|
cmd := exec.Command(command[0], command[1:]...)
|
|
|
|
cmd.Stdout = os.Stdout
|
|
|
|
cmd.Stderr = os.Stderr
|
|
|
|
|
|
|
|
err = cmd.Run()
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal("Error: ", err)
|
|
|
|
}
|
2023-07-12 20:45:29 +02:00
|
|
|
}
|