package main import ( "bufio" "flag" "fmt" "io" "os" "os/exec" "path/filepath" "strings" ) func main() { var appName string var binaryName string var configFile string var macOSMinimumSystemVersion string var manifestPath string var outDir string var wailsPath string flag.StringVar(&appName, "app-name", "", "") flag.StringVar(&binaryName, "binary-name", "", "") flag.StringVar(&configFile, "config-file", "config.yml", "") flag.StringVar(&macOSMinimumSystemVersion, "macos-minimum-system-version", "", "") flag.StringVar(&manifestPath, "manifest", "", "") flag.StringVar(&outDir, "out", "", "") flag.StringVar(&wailsPath, "wails", "", "") flag.Parse() require(manifestPath != "", "missing --manifest") require(outDir != "", "missing --out") require(wailsPath != "", "missing --wails") require(appName != "", "missing --app-name") require(binaryName != "", "missing --binary-name") var err error wailsPath, err = filepath.Abs(wailsPath) must(err) must(resolveGoEnvToAbsolutePath()) tempRoot, err := os.MkdirTemp("", "rules-wails-build-assets-*") must(err) defer func() { _ = os.Chmod(tempRoot, 0o755) _ = os.RemoveAll(tempRoot) }() workDir := filepath.Join(tempRoot, "work") homeDir := filepath.Join(tempRoot, "home") must(os.MkdirAll(workDir, 0o755)) must(os.MkdirAll(homeDir, 0o755)) must(stageManifest(manifestPath, workDir)) command := exec.Command( wailsPath, "update", "build-assets", "-name", appName, "-binaryname", binaryName, "-config", filepath.Join(workDir, configFile), "-dir", workDir, ) command.Dir = workDir command.Env = append(os.Environ(), "HOME="+homeDir, "LC_ALL=C", "TZ=UTC", ) command.Stdout = os.Stdout command.Stderr = os.Stderr must(command.Run()) updateMacOSMinimumVersion(filepath.Join(workDir, "darwin", "Info.plist"), macOSMinimumSystemVersion) updateMacOSMinimumVersion(filepath.Join(workDir, "darwin", "Info.dev.plist"), macOSMinimumSystemVersion) must(os.RemoveAll(outDir)) must(os.MkdirAll(outDir, 0o755)) must(copyTree(workDir, outDir)) } func resolveGoEnvToAbsolutePath() error { goBinary := os.Getenv("GO_BIN") if goBinary == "" || filepath.IsAbs(goBinary) { return nil } absolutePath, err := filepath.Abs(goBinary) if err != nil { return err } return os.Setenv("GO_BIN", absolutePath) } func updateMacOSMinimumVersion(path string, minimumVersion string) { if minimumVersion == "" { return } if _, err := os.Stat(path); err != nil { return } if _, err := os.Stat("/usr/libexec/PlistBuddy"); err != nil { return } setCommand := exec.Command("/usr/libexec/PlistBuddy", "-c", "Set :LSMinimumSystemVersion "+minimumVersion, path) if err := setCommand.Run(); err == nil { return } addCommand := exec.Command("/usr/libexec/PlistBuddy", "-c", "Add :LSMinimumSystemVersion string "+minimumVersion, path) _ = addCommand.Run() } func stageManifest(manifestPath string, destinationRoot string) error { entries, err := readManifest(manifestPath) if err != nil { return err } for _, entry := range entries { destinationPath := filepath.Join(destinationRoot, entry.relativePath) if err := os.MkdirAll(filepath.Dir(destinationPath), 0o755); err != nil { return err } if err := copyFile(entry.sourcePath, destinationPath); err != nil { return err } } return nil } func readManifest(path string) ([]manifestEntry, error) { file, err := os.Open(path) if err != nil { return nil, err } defer file.Close() entries := make([]manifestEntry, 0) scanner := bufio.NewScanner(file) for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) if line == "" { continue } parts := strings.SplitN(line, "\t", 2) if len(parts) != 2 { return nil, fmt.Errorf("invalid manifest line: %s", line) } entries = append(entries, manifestEntry{ sourcePath: parts[0], relativePath: parts[1], }) } return entries, scanner.Err() } func copyTree(sourceRoot string, destinationRoot string) error { return filepath.Walk(sourceRoot, func(path string, info os.FileInfo, err error) error { if err != nil { return err } relativePath, err := filepath.Rel(sourceRoot, path) if err != nil { return err } if relativePath == "." { return nil } destinationPath := filepath.Join(destinationRoot, relativePath) if info.IsDir() { return os.MkdirAll(destinationPath, 0o755) } if err := os.MkdirAll(filepath.Dir(destinationPath), 0o755); err != nil { return err } return copyFile(path, destinationPath) }) } func copyFile(sourcePath string, destinationPath string) error { sourceFile, err := os.Open(sourcePath) if err != nil { return err } defer sourceFile.Close() destinationFile, err := os.Create(destinationPath) if err != nil { return err } defer destinationFile.Close() if _, err := io.Copy(destinationFile, sourceFile); err != nil { return err } return destinationFile.Chmod(0o644) } func must(err error) { if err != nil { panic(err) } } func require(condition bool, message string) { if !condition { panic(message) } } type manifestEntry struct { sourcePath string relativePath string }