@@ -172,77 +172,94 @@ func InstallWithProgress(cliPkgs, caskPkgs []string, dryRun bool) (installedForm
172172 }
173173
174174 if len (newCask ) > 0 {
175- for _ , pkg := range newCask {
176- progress .SetCurrent (pkg )
177- progress .PrintLine (" Installing %s..." , pkg )
178- start := time .Now ()
179- errMsg := installCaskWithProgress (pkg , progress )
180- elapsed := time .Since (start )
181- progress .IncrementWithStatus (errMsg == "" )
182- duration := ui .FormatDuration (elapsed )
183- if errMsg == "" {
184- progress .PrintLine (" %s %s" , ui .Green ("✔ " + pkg ), ui .Cyan ("(" + duration + ")" ))
185- installedCasks = append (installedCasks , pkg )
186- } else {
187- progress .PrintLine (" %s %s" , ui .Red ("✗ " + pkg + " (" + errMsg + ")" ), ui .Cyan ("(" + duration + ")" ))
188- allFailed = append (allFailed , failedJob {
189- installJob : installJob {name : pkg , isCask : true },
190- errMsg : errMsg ,
191- })
192- }
193- }
175+ caskInstalled , caskFailed := installCasksWithProgress (newCask , progress )
176+ installedCasks = append (installedCasks , caskInstalled ... )
177+ allFailed = append (allFailed , caskFailed ... )
194178 }
195179
196180 progress .Finish ()
197181
198- if len (allFailed ) > 0 {
199- fmt .Printf ("\n Retrying %d failed packages...\n " , len (allFailed ))
182+ allFailed = retryFailedJobs (allFailed , & installedFormulae , & installedCasks , aliasMap )
200183
201- for _ , f := range allFailed {
202- var errMsg string
203- if f .isCask {
204- errMsg = installSmartCaskWithError (f .name )
205- } else {
206- errMsg = installFormulaWithError (f .name )
207- }
208- if errMsg == "" {
209- fmt .Printf (" ✔ %s (retry succeeded)\n " , f .name )
210- if f .isCask {
211- installedCasks = append (installedCasks , f .name )
212- } else {
213- installedFormulae = append (installedFormulae , aliasMap [f .name ])
214- }
215- } else {
216- fmt .Printf (" ✗ %s (still failed)\n " , f .name )
217- }
218- }
184+ handleFailedJobs (allFailed )
219185
220- succeededFormulae := make (map [string ]bool , len (installedFormulae ))
221- for _ , p := range installedFormulae {
222- succeededFormulae [p ] = true
186+ return installedFormulae , installedCasks , nil
187+ }
188+
189+ // installCasksWithProgress installs cask packages one by one with progress reporting.
190+ // Returns the successfully installed cask names and any failed jobs.
191+ func installCasksWithProgress (pkgs []string , progress * ui.StickyProgress ) (installed []string , failed []failedJob ) {
192+ for _ , pkg := range pkgs {
193+ progress .SetCurrent (pkg )
194+ progress .PrintLine (" Installing %s..." , pkg )
195+ start := time .Now ()
196+ errMsg := installCaskWithProgress (pkg , progress )
197+ elapsed := time .Since (start )
198+ progress .IncrementWithStatus (errMsg == "" )
199+ duration := ui .FormatDuration (elapsed )
200+ if errMsg == "" {
201+ progress .PrintLine (" %s %s" , ui .Green ("✔ " + pkg ), ui .Cyan ("(" + duration + ")" ))
202+ installed = append (installed , pkg )
203+ } else {
204+ progress .PrintLine (" %s %s" , ui .Red ("✗ " + pkg + " (" + errMsg + ")" ), ui .Cyan ("(" + duration + ")" ))
205+ failed = append (failed , failedJob {
206+ installJob : installJob {name : pkg , isCask : true },
207+ errMsg : errMsg ,
208+ })
223209 }
224- succeededCasks := make (map [string ]bool , len (installedCasks ))
225- for _ , p := range installedCasks {
226- succeededCasks [p ] = true
210+ }
211+ return installed , failed
212+ }
213+
214+ // retryFailedJobs retries any failed installations once and updates the installed
215+ // slices in-place. Returns the subset that still failed after the retry.
216+ func retryFailedJobs (allFailed []failedJob , installedFormulae , installedCasks * []string , aliasMap map [string ]string ) []failedJob {
217+ if len (allFailed ) == 0 {
218+ return nil
219+ }
220+
221+ fmt .Printf ("\n Retrying %d failed packages...\n " , len (allFailed ))
222+
223+ for _ , f := range allFailed {
224+ var errMsg string
225+ if f .isCask {
226+ errMsg = installSmartCaskWithError (f .name )
227+ } else {
228+ errMsg = installFormulaWithError (f .name )
227229 }
228- var stillFailed [] failedJob
229- for _ , f := range allFailed {
230+ if errMsg == "" {
231+ fmt . Printf ( " ✔ %s (retry succeeded) \n " , f . name )
230232 if f .isCask {
231- if ! succeededCasks [f .name ] {
232- stillFailed = append (stillFailed , f )
233- }
233+ * installedCasks = append (* installedCasks , f .name )
234234 } else {
235- if ! succeededFormulae [aliasMap [f.name ]] {
236- stillFailed = append (stillFailed , f )
237- }
235+ * installedFormulae = append (* installedFormulae , aliasMap [f .name ])
238236 }
237+ } else {
238+ fmt .Printf (" ✗ %s (still failed)\n " , f .name )
239239 }
240- allFailed = stillFailed
241240 }
242241
243- handleFailedJobs (allFailed )
244-
245- return installedFormulae , installedCasks , nil
242+ succeededFormulae := make (map [string ]bool , len (* installedFormulae ))
243+ for _ , p := range * installedFormulae {
244+ succeededFormulae [p ] = true
245+ }
246+ succeededCasks := make (map [string ]bool , len (* installedCasks ))
247+ for _ , p := range * installedCasks {
248+ succeededCasks [p ] = true
249+ }
250+ var stillFailed []failedJob
251+ for _ , f := range allFailed {
252+ if f .isCask {
253+ if ! succeededCasks [f .name ] {
254+ stillFailed = append (stillFailed , f )
255+ }
256+ } else {
257+ if ! succeededFormulae [aliasMap [f.name ]] {
258+ stillFailed = append (stillFailed , f )
259+ }
260+ }
261+ }
262+ return stillFailed
246263}
247264
248265func handleFailedJobs (failed []failedJob ) {
@@ -312,7 +329,7 @@ func installCaskWithProgress(pkg string, progress *ui.StickyProgress) string {
312329}
313330
314331func brewInstallCmd (args ... string ) * exec.Cmd {
315- cmd := exec .Command ("brew" , args ... )
332+ cmd := exec .Command ("brew" , args ... ) //nolint:gosec // "brew" is a hardcoded binary; args are package names validated by caller
316333 cmd .Env = append (os .Environ (), "HOMEBREW_NO_AUTO_UPDATE=1" )
317334 return cmd
318335}
@@ -456,7 +473,7 @@ func ResolveFormulaNames(names []string) map[string]string {
456473 }
457474
458475 args := append ([]string {"info" , "--json" }, names ... )
459- cmd := exec .Command ("brew" , args ... )
476+ cmd := exec .Command ("brew" , args ... ) //nolint:gosec // "brew" is a hardcoded binary; args are package names
460477 output , err := cmd .Output ()
461478 if err != nil {
462479 return identityMap (names )
0 commit comments