package executil import ( "bytes" "fmt" "os" "os/exec" "path" "strings" "zlh-agent/scripts" ) // RunEmbeddedScript executes an embedded script via bash by piping its contents to stdin. // If the script is a devcontainer installer, this automatically prepends the shared common.sh // so that tiny per-runtime installers can call install_runtime in the same shell session. func RunEmbeddedScript(scriptPath string, extraEnv ...string) error { normalized := normalizeEmbeddedPath(scriptPath) payload, err := loadEmbeddedPayload(normalized) if err != nil { return err } cmd := exec.Command("bash") cmd.Stdin = bytes.NewReader(payload) // Inherit current env and overlay any provided vars (e.g., RUNTIME_VERSION=24). if len(extraEnv) > 0 { cmd.Env = append(os.Environ(), extraEnv...) } // Match RunScript behavior (executil.go) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr return cmd.Run() } func loadEmbeddedPayload(normalized string) ([]byte, error) { // If this is a devcontainer installer, prepend the shared library. if strings.HasPrefix(normalized, "devcontainer/") { commonPath := "devcontainer/lib/common.sh" common, err := scripts.FS.ReadFile(commonPath) if err != nil { return nil, fmt.Errorf("embedded script missing: %s: %w", commonPath, err) } body, err := scripts.FS.ReadFile(normalized) if err != nil { return nil, fmt.Errorf("embedded script missing: %s: %w", normalized, err) } // Ensure bash sees common first, then runtime installer. return append(append(common, '\n'), body...), nil } data, err := scripts.FS.ReadFile(normalized) if err != nil { return nil, fmt.Errorf("embedded script missing: %s: %w", normalized, err) } return data, nil } func normalizeEmbeddedPath(p string) string { p = strings.TrimPrefix(p, "scripts/") p = path.Clean(p) return strings.TrimPrefix(p, "/") }