package main import ( "flag" "fmt" "os" "os/exec" "path/filepath" "runtime" "strings" ) func main() { var bunDarwinAarch64 string var bunDarwinX64 string var bunLinuxAarch64 string var bunLinuxX64 string var bunWindowsX64 string var packageDir string var script string flag.StringVar(&bunDarwinAarch64, "bun-darwin-aarch64", "", "") flag.StringVar(&bunDarwinX64, "bun-darwin-x64", "", "") flag.StringVar(&bunLinuxAarch64, "bun-linux-aarch64", "", "") flag.StringVar(&bunLinuxX64, "bun-linux-x64", "", "") flag.StringVar(&bunWindowsX64, "bun-windows-x64", "", "") flag.StringVar(&packageDir, "package-dir", "", "") flag.StringVar(&script, "script", "dev", "") flag.Parse() require(packageDir != "", "missing --package-dir") bunPath, err := resolveBunBinary(bunDarwinAarch64, bunDarwinX64, bunLinuxAarch64, bunLinuxX64, bunWindowsX64) must(err) must(makeAbsolute(&bunPath)) workspaceRoot := os.Getenv("BUILD_WORKSPACE_DIRECTORY") if workspaceRoot == "" { returnError("BUILD_WORKSPACE_DIRECTORY is required for frontend dev sessions") } packageRoot := filepath.Join(workspaceRoot, filepath.FromSlash(packageDir)) if _, err := os.Stat(filepath.Join(packageRoot, "package.json")); err != nil { returnError("frontend package.json not found in workspace package dir: " + packageRoot) } environment := append([]string{}, os.Environ()...) environment = setPath(environment, buildPath(workspaceRoot, packageRoot)) command := exec.Command(bunPath, "--bun", "run", script) command.Dir = packageRoot command.Env = environment command.Stdout = os.Stdout command.Stderr = os.Stderr command.Stdin = os.Stdin if err := command.Run(); err != nil { if exitError, ok := err.(*exec.ExitError); ok { os.Exit(exitError.ExitCode()) } panic(err) } } func resolveBunBinary(darwinAarch64 string, darwinX64 string, linuxAarch64 string, linuxX64 string, windowsX64 string) (string, error) { switch runtime.GOOS + "/" + runtime.GOARCH { case "darwin/arm64": return darwinAarch64, nil case "darwin/amd64": return darwinX64, nil case "linux/arm64": return linuxAarch64, nil case "linux/amd64": return linuxX64, nil case "windows/amd64": return windowsX64, nil default: return "", fmt.Errorf("unsupported Bun exec platform: %s/%s", runtime.GOOS, runtime.GOARCH) } } func buildPath(workspaceRoot string, packageRoot string) string { entries := make([]string, 0, 4) for _, candidate := range []string{ filepath.Join(packageRoot, "node_modules", ".bin"), filepath.Join(workspaceRoot, "node_modules", ".bin"), os.Getenv("PATH"), } { if candidate != "" { entries = append(entries, candidate) } } return strings.Join(entries, string(os.PathListSeparator)) } func setPath(environment []string, pathValue string) []string { return setEnv(environment, "PATH", pathValue) } func setEnv(environment []string, key string, value string) []string { prefix := key + "=" for index, entry := range environment { if strings.HasPrefix(entry, prefix) { environment[index] = prefix + value return environment } } return append(environment, prefix+value) } func makeAbsolute(path *string) error { if filepath.IsAbs(*path) { return nil } absolutePath, err := filepath.Abs(*path) if err != nil { return err } *path = absolutePath return nil } func returnError(message string) { fmt.Fprintln(os.Stderr, message) os.Exit(1) } func must(err error) { if err != nil { panic(err) } } func require(condition bool, message string) { if !condition { panic(message) } }