11package system
22
33import (
4- "crypto/sha256"
5- "encoding/hex"
64 "fmt"
7- "io"
8- "net/http"
95 "os"
106 "os/exec"
11- "runtime"
127 "strings"
13-
14- "github.com/openbootdotdev/openboot/internal/httputil"
158)
169
1710func HomeDir () (string , error ) {
@@ -22,32 +15,6 @@ func HomeDir() (string, error) {
2215 return home , nil
2316}
2417
25- func Architecture () string {
26- return runtime .GOARCH
27- }
28-
29- func HomebrewPrefix () string {
30- if Architecture () == "arm64" {
31- return "/opt/homebrew"
32- }
33- return "/usr/local"
34- }
35-
36- func IsHomebrewInstalled () bool {
37- _ , err := exec .LookPath ("brew" )
38- return err == nil
39- }
40-
41- func IsXcodeCliInstalled () bool {
42- cmd := exec .Command ("xcode-select" , "-p" )
43- return cmd .Run () == nil
44- }
45-
46- func IsGumInstalled () bool {
47- _ , err := exec .LookPath ("gum" )
48- return err == nil
49- }
50-
5118func RunCommand (name string , args ... string ) error {
5219 cmd := exec .Command (name , args ... ) //nolint:gosec // intentional generic runner; callers are responsible for validating name and args
5320 cmd .Stdout = os .Stdout
@@ -70,77 +37,6 @@ func RunCommandOutput(name string, args ...string) (string, error) {
7037 return strings .TrimSpace (string (output )), err
7138}
7239
73- // knownBrewInstallHash is the SHA256 of the Homebrew install script pinned on
74- // 2026-04-19 (Homebrew/install HEAD as of that date). Update this constant
75- // whenever the installer script changes upstream by running:
76- //
77- // curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh | sha256sum
78- const knownBrewInstallHash = "dfd5145fe2aa5956a600e35848765273f5798ce6def01bd08ecec088a1268d91"
79-
80- const brewInstallURL = "https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh"
81-
82- // brewHTTPClient is a var so tests can inject a mock transport.
83- var brewHTTPClient * http.Client = http .DefaultClient
84-
85- func InstallHomebrew () error {
86- // Download the installer via httputil.Do so rate-limit handling is applied.
87- req , err := http .NewRequest (http .MethodGet , brewInstallURL , nil )
88- if err != nil {
89- return fmt .Errorf ("create homebrew install request: %w" , err )
90- }
91- resp , err := httputil .Do (brewHTTPClient , req )
92- if err != nil {
93- return fmt .Errorf ("download homebrew install script: %w" , err )
94- }
95- defer resp .Body .Close ()
96-
97- if resp .StatusCode != http .StatusOK {
98- return fmt .Errorf ("download homebrew install script: unexpected status %d" , resp .StatusCode )
99- }
100-
101- scriptBytes , err := io .ReadAll (resp .Body )
102- if err != nil {
103- return fmt .Errorf ("read homebrew install script: %w" , err )
104- }
105-
106- // Verify SHA256 before executing anything.
107- sum := sha256 .Sum256 (scriptBytes )
108- got := hex .EncodeToString (sum [:])
109- if got != knownBrewInstallHash {
110- return fmt .Errorf ("homebrew installer SHA256 mismatch: refusing to execute" )
111- }
112-
113- // Write verified script to a temp file, execute, then clean up.
114- tmpFile , err := os .CreateTemp ("" , "homebrew-install-*.sh" )
115- if err != nil {
116- return fmt .Errorf ("create temp file for homebrew install: %w" , err )
117- }
118- defer os .Remove (tmpFile .Name ())
119-
120- if _ , err := tmpFile .Write (scriptBytes ); err != nil {
121- tmpFile .Close () //nolint:gosec,errcheck // error-path cleanup; original write error takes precedence
122- return fmt .Errorf ("write homebrew install script: %w" , err )
123- }
124- if err := tmpFile .Close (); err != nil {
125- return fmt .Errorf ("close homebrew install script: %w" , err )
126- }
127-
128- if err := os .Chmod (tmpFile .Name (), 0700 ); err != nil { //nolint:gosec // install script must be executable
129- return fmt .Errorf ("chmod homebrew install script: %w" , err )
130- }
131-
132- tty , opened := OpenTTY ()
133- if opened {
134- defer tty .Close () //nolint:errcheck // best-effort TTY cleanup
135- }
136-
137- cmd := exec .Command (tmpFile .Name ()) //nolint:gosec // script content is SHA256-verified before execution
138- cmd .Stdout = os .Stdout
139- cmd .Stderr = os .Stderr
140- cmd .Stdin = tty
141- return cmd .Run ()
142- }
143-
14440func GetGitConfig (key string ) string {
14541 // Try global first (most common)
14642 output , err := RunCommandSilent ("git" , "config" , "--global" , key )
0 commit comments