package ui import ( "errors" "fmt" "os" ) type exitCoder interface { ExitCode() int } // transparentExit is satisfied by *runner.ChildExitError without importing it. type transparentExit interface { error Transparent() bool ExitCode() int IsSignaled() bool } // scrubbedEnvReporter is optionally satisfied by a transparent exit error to // surface how many environment variables the sandbox scrubbed from the failed // run. Kept separate from transparentExit so older implementations still // classify as transparent. type scrubbedEnvReporter interface { ScrubbedEnvCount() int } type exitDecision struct { transparent bool notice bool code int message string } // classifyExit is the pure decision behind ExitFromCommandError. The notice is // suppressed for signal exits (the user initiated the interrupt) and in silent // mode. func classifyExit(err error) exitDecision { var te transparentExit if !errors.As(err, &te) || !te.Transparent() { return exitDecision{} } d := exitDecision{transparent: true, code: te.ExitCode()} if !te.IsSignaled() && verbosityLevel != VerbosityLevelSilent { d.notice = true d.message = "↳ pmg: " + te.Error() // Env scrubbing produces no sandbox violation and the child's own // error (e.g. a registry 401) does not point at the cause, so hint at // it here. Names are not printed; they are at info level via --debug. if sr, ok := te.(scrubbedEnvReporter); ok && sr.ScrubbedEnvCount() > 0 { d.message += fmt.Sprintf( "\n↳ pmg: sandbox scrubbed %d env var(s) (names via --debug, re-allow with --sandbox-allow env=NAME)", sr.ScrubbedEnvCount()) } } return d } // ExitFromCommandError is the single exit point for package-manager commands. A // child that exited on its own is mirrored transparently; everything else keeps // the visible PMG error framing. func ExitFromCommandError(err error) { if err == nil { return } if d := classifyExit(err); d.transparent { ClearStatus() if d.notice { fmt.Fprintln(os.Stderr, Colors.Dim(d.message)) } os.Exit(d.code) } var ec exitCoder if errors.As(err, &ec) { ErrorExitWithCode(err, ec.ExitCode()) return } ErrorExit(err) }