package main import ( "bytes" "crypto/rand" "encoding/base32" "fmt" "html/template" "io" "os" "os/exec" "path/filepath" "reflect" "slices" "strings" "codeberg.org/marcig/riploy/slag" ) var ( ErrNoHash = fmt.Errorf("must provide a valid hash (docker tag) for the template") ErrNoServerPort = fmt.Errorf("must provide a port for the server (-server-port)") ErrNoFrontendPort = fmt.Errorf("must provide a port for the frontend (-svelte-port)") ErrNoDebuggerPort = fmt.Errorf("must provide a port for the debugger (-debugger-port)") ErrNoDockerhubUserName = fmt.Errorf("docker hub username must be provided (-docker-user)") ErrNoProjectName = fmt.Errorf("project name must be provided (-stack-name)") ErrNoVCS = fmt.Errorf("must provide a vcs url (ex: tangled.org/marcig.com/riploy) (-vcs)") ErrNoDomain = fmt.Errorf("project name must be provided (-domain)") ) func RandomHash(length int) (string, error) { randomBytes := make([]byte, length) _, err := rand.Read(randomBytes) if err != nil { return "", err } token := base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(randomBytes) return token, nil } func exitln(v any) { fmt.Printf("%v\n", v) os.Exit(1) } func exit() { os.Exit(1) } func warnln(format string, a ...any) { warnFormat := fmt.Sprintf("[WARN]: %v\n", format) fmt.Printf(warnFormat, a...) } func errorln(format string, a ...any) { warnFormat := fmt.Sprintf("[ERROR]: %v\n", format) fmt.Printf(warnFormat, a...) } func exitIfAnyError(errs []error) { if errs == nil { return } for _, e := range errs { fmt.Printf("%v\n", e) } os.Exit(1) } func okExit(v any) { fmt.Printf("%v\n", v) os.Exit(0) } func isFlagOn(f *bool) bool { return *f } func isFlagOff(f *bool) bool { return !*f } func runOrDie(name string, arg ...string) { run := exec.Command(name, arg...) run.Stdout = os.Stdout run.Stderr = os.Stderr fmt.Printf("running: [%v]\n", run) err := run.Run() if err != nil { exitln(err) } } func runGetOrDie(name string, arg ...string) []byte { var b []byte out := bytes.NewBuffer(b) run := exec.Command(name, arg...) run.Stdout = out run.Stderr = os.Stderr fmt.Printf("%v\n", run) err := run.Run() if err != nil { exitln(err) } o, err := io.ReadAll(out) if err != nil { exitln(err) } return o } func readFileOrDie(name string) []byte { b, err := os.ReadFile(name) if err != nil { exitln(err) } return b } func getHome() string { home, ok := os.LookupEnv("HOME") if !ok { exitln("no home env") } return home } func writeFileString(filename string, content string, shouldOverwrite bool) { if shouldOverwrite { err := os.WriteFile(filename, []byte(content), 0666) if err != nil { exitln(err) } } } func writeFileOrDie(filename string, content []byte) { err := os.WriteFile(filename, []byte(content), 0666) if err != nil { exitln(err) } } func createFileOrDie(filename string) *os.File { f, err := os.Create(filename) if err != nil { exitln(err) } return f } func pwd() string { path, err := os.Getwd() if err != nil { exitln(err) } return path } func cd(dir string) { err := os.Chdir(dir) if err != nil { exitln(err) } } // @Warn: i think docker requires you to just use _ for stack names func generateNameFromDir(separator string) string { genericDirs := []string{"frontend", "backend"} cwd := pwd() path := strings.Split(cwd, "/") base := filepath.Base(cwd) if slices.Contains(genericDirs, base) { parent := path[len(path)-2] return fmt.Sprintf("%v%v%v", parent, separator, base) } else { return base } } func getIdentityNameFromDir() string { genericDirs := []string{"frontend", "backend"} cwd := pwd() path := strings.Split(cwd, "/") base := filepath.Base(cwd) if slices.Contains(genericDirs, base) { parent := path[len(path)-2] return parent } else { return base } } func sourceFile() string { ex, err := os.Executable() if err != nil { exitln(err) } exPath := filepath.Dir(ex) return exPath } func getFileChecksumOrDie(filename string) string { sum := runGetOrDie("md5sum", filename) parts := bytes.Split(sum, []byte{' '}) return string(parts[0]) } func createFileFromTemplate(tmpl *template.Template, templateName string, filepath string, data any) error { f := createFileOrDie(filepath) err := tmpl.ExecuteTemplate(f, templateName, data) return err } // @Warn this is not deep! func mergo(dst, src any) error { tmp := reflect.ValueOf(dst) kind := tmp.Kind() if kind != reflect.Pointer { return slag.ErrNoPointer } out := tmp.Elem() if out.Kind() != reflect.Struct { return slag.ErrNoStructUnderneath } rSrc := reflect.ValueOf(src) for f, v := range out.Fields() { sf := rSrc.FieldByName(f.Name) if v.IsZero() { v.Set(sf) } } return nil }