@@ -151,7 +151,7 @@ func (e *Engine) Run(ctx context.Context, ch chan<- domain.Event, _ <-chan domai
151151 Severity : domain .SeverityWarn ,
152152 Payload : domain.StepDonePayload {OK : false },
153153 })
154- } else {
154+ } else {
155155 tag := releaseInfo .Tag
156156 if tag == "" && releaseInfo .HighestVersion != "" {
157157 tag = "v" + releaseInfo .HighestVersion
@@ -180,83 +180,83 @@ func (e *Engine) Run(ctx context.Context, ch chan<- domain.Event, _ <-chan domai
180180 Unit : "pct" ,
181181 },
182182 })
183- _ = emit (domain.Event {
184- Type : domain .EventStepDone ,
185- StepID : releaseStepID ,
186- Source : "mock" ,
187- Severity : domain .SeverityInfo ,
188- Payload : releaseInfo ,
189- })
190- }
183+ _ = emit (domain.Event {
184+ Type : domain .EventStepDone ,
185+ StepID : releaseStepID ,
186+ Source : "mock" ,
187+ Severity : domain .SeverityInfo ,
188+ Payload : releaseInfo ,
189+ })
190+ }
191+
192+ // Step: check system status via PHP adapter.
193+ const sysStepID = "check_system_status"
194+ _ = emit (domain.Event {
195+ Type : domain .EventStepStart ,
196+ StepID : sysStepID ,
197+ Source : "mock" ,
198+ Severity : domain .SeverityInfo ,
199+ Payload : domain.StepStartPayload {
200+ Label : "Check system status" ,
201+ Index : 0 ,
202+ Total : 0 ,
203+ },
204+ })
205+ _ = emit (domain.Event {
206+ Type : domain .EventLog ,
207+ StepID : sysStepID ,
208+ Source : "mock" ,
209+ Severity : domain .SeverityInfo ,
210+ Payload : domain.LogPayload {
211+ Message : "Checking system status…" ,
212+ },
213+ })
191214
192- // Step: check system status via PHP adapter.
193- const sysStepID = "check_system_status"
215+ status , err := fetchSystemStatus ( ctx )
216+ if err != nil {
194217 _ = emit (domain.Event {
195- Type : domain .EventStepStart ,
218+ Type : domain .EventWarning ,
196219 StepID : sysStepID ,
197220 Source : "mock" ,
198- Severity : domain .SeverityInfo ,
199- Payload : domain.StepStartPayload {
200- Label : "Check system status" ,
201- Index : 0 ,
202- Total : 0 ,
221+ Severity : domain .SeverityWarn ,
222+ Payload : domain.LogPayload {
223+ Message : "Unable to check system status; continuing…" ,
224+ Fields : map [string ]string {"error" : err .Error ()},
203225 },
204226 })
205227 _ = emit (domain.Event {
206- Type : domain .EventLog ,
228+ Type : domain .EventSystemStatus ,
207229 StepID : sysStepID ,
208230 Source : "mock" ,
209- Severity : domain .SeverityInfo ,
210- Payload : domain.LogPayload {
211- Message : "Checking system status…" ,
231+ Severity : domain .SeverityWarn ,
232+ Payload : domain.SystemStatus {
233+ Items : nil ,
234+ UpdatedAt : time .Now (),
212235 },
213236 })
214-
215- status , err := fetchSystemStatus (ctx )
216- if err != nil {
217- _ = emit (domain.Event {
218- Type : domain .EventWarning ,
219- StepID : sysStepID ,
220- Source : "mock" ,
221- Severity : domain .SeverityWarn ,
222- Payload : domain.LogPayload {
223- Message : "Unable to check system status; continuing…" ,
224- Fields : map [string ]string {"error" : err .Error ()},
225- },
226- })
227- _ = emit (domain.Event {
228- Type : domain .EventSystemStatus ,
229- StepID : sysStepID ,
230- Source : "mock" ,
231- Severity : domain .SeverityWarn ,
232- Payload : domain.SystemStatus {
233- Items : nil ,
234- UpdatedAt : time .Now (),
235- },
236- })
237- _ = emit (domain.Event {
238- Type : domain .EventStepDone ,
239- StepID : sysStepID ,
240- Source : "mock" ,
241- Severity : domain .SeverityWarn ,
242- Payload : domain.StepDonePayload {OK : false },
243- })
244- } else {
245- _ = emit (domain.Event {
246- Type : domain .EventSystemStatus ,
247- StepID : sysStepID ,
248- Source : "mock" ,
249- Severity : domain .SeverityInfo ,
250- Payload : status ,
251- })
252- _ = emit (domain.Event {
253- Type : domain .EventStepDone ,
254- StepID : sysStepID ,
255- Source : "mock" ,
256- Severity : domain .SeverityInfo ,
257- Payload : domain.StepDonePayload {OK : true },
258- })
259- }
237+ _ = emit (domain.Event {
238+ Type : domain .EventStepDone ,
239+ StepID : sysStepID ,
240+ Source : "mock" ,
241+ Severity : domain .SeverityWarn ,
242+ Payload : domain.StepDonePayload {OK : false },
243+ })
244+ } else {
245+ _ = emit (domain.Event {
246+ Type : domain .EventSystemStatus ,
247+ StepID : sysStepID ,
248+ Source : "mock" ,
249+ Severity : domain .SeverityInfo ,
250+ Payload : status ,
251+ })
252+ _ = emit (domain.Event {
253+ Type : domain .EventStepDone ,
254+ StepID : sysStepID ,
255+ Source : "mock" ,
256+ Severity : domain .SeverityInfo ,
257+ Payload : domain.StepDonePayload {OK : true },
258+ })
259+ }
260260
261261 // A sample question to drive UI selection (engine does not consume the answer yet).
262262 _ = emit (domain.Event {
@@ -369,7 +369,7 @@ func (e *Engine) Run(ctx context.Context, ch chan<- domain.Event, _ <-chan domai
369369 StepID : s .id ,
370370 Source : "mock" ,
371371 Severity : domain .SeverityInfo ,
372- Payload : domain.StepDonePayload {OK : true },
372+ Payload : domain.StepDonePayload {OK : true },
373373 })
374374 }
375375 }()
@@ -389,7 +389,7 @@ func envInt(key string, def int) int {
389389
390390type systemStatusJSON struct {
391391 Overall string `json:"overall"`
392- Items []struct {
392+ Items []struct {
393393 Key string `json:"key"`
394394 Label string `json:"label"`
395395 Level string `json:"level"`
@@ -403,6 +403,9 @@ func fetchSystemStatus(ctx context.Context) (domain.SystemStatus, error) {
403403 return domain.SystemStatus {}, err
404404 }
405405
406+ ctx , cancel := context .WithTimeout (ctx , 25 * time .Second )
407+ defer cancel ()
408+
406409 cmd := exec .CommandContext (ctx , "php" , entry , "system-status" , "--format=json" , "--no-ansi" , "--no-interaction" )
407410 var stderr bytes.Buffer
408411 cmd .Stderr = & stderr
@@ -455,14 +458,60 @@ func fetchSystemStatus(ctx context.Context) (domain.SystemStatus, error) {
455458}
456459
457460func findPHPInstallerCLIEntry () (string , error ) {
458- candidates := []string {
461+ candidates := []string {}
462+
463+ // Prefer the bootstrapper on PATH (installed alongside the Go binary).
464+ if p , err := exec .LookPath ("evo" ); err == nil && p != "" {
465+ candidates = append (candidates , p )
466+ }
467+
468+ // Prefer a sibling `evo` script next to the running executable (common install layout).
469+ if exe , err := os .Executable (); err == nil && exe != "" {
470+ exeDir := filepath .Dir (exe )
471+ if exeDir != "" && exeDir != "." {
472+ candidates = append (candidates , filepath .Join (exeDir , "evo" ))
473+ }
474+ }
475+
476+ // Repo-local fallbacks (when running from source checkout).
477+ candidates = append (candidates ,
459478 filepath .Join ("installer" , "bin" , "evo" ),
460479 filepath .Join ("bin" , "evo" ),
461- }
480+ )
462481 for _ , p := range candidates {
463- if fi , err := os .Stat (p ); err == nil && ! fi .IsDir () {
464- return p , nil
482+ if strings .TrimSpace (p ) == "" {
483+ continue
484+ }
485+ fi , err := os .Stat (p )
486+ if err != nil || fi .IsDir () {
487+ continue
465488 }
489+ if ! looksLikePHPScript (p ) {
490+ continue
491+ }
492+ return p , nil
466493 }
467494 return "" , fmt .Errorf ("unable to find installer PHP CLI entry (tried: %s)" , strings .Join (candidates , ", " ))
468495}
496+
497+ func looksLikePHPScript (path string ) bool {
498+ f , err := os .Open (path )
499+ if err != nil {
500+ return false
501+ }
502+ defer f .Close ()
503+
504+ buf := make ([]byte , 256 )
505+ n , _ := f .Read (buf )
506+ if n <= 0 {
507+ return false
508+ }
509+ head := strings .ToLower (string (buf [:n ]))
510+ if strings .Contains (head , "<?php" ) {
511+ return true
512+ }
513+ if strings .HasPrefix (head , "#!" ) && strings .Contains (head , "php" ) {
514+ return true
515+ }
516+ return false
517+ }
0 commit comments