Skip to content

Commit 0aec5fa

Browse files
committed
[FIX] TUI.
1 parent 99511e5 commit 0aec5fa

File tree

4 files changed

+192
-48
lines changed

4 files changed

+192
-48
lines changed

cmd/evo/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ func printUsage() {
254254
fmt.Println("")
255255
fmt.Println("Common flags:")
256256
fmt.Println(" -f, --force Force installation even if directory exists")
257-
fmt.Println(" --branch=<name> Install from Git branch (e.g., 3.5.x)")
257+
fmt.Println(" --branch=<name> Install from Git branch (e.g., main or master)")
258258
fmt.Println(" --db-type=<driver> mysql|pgsql|sqlite|sqlsrv")
259259
fmt.Println(" --db-name=<name|path> Database name (or SQLite file path)")
260260
fmt.Println(" --admin-email=<email> Admin email")

internal/engine/install/engine.go

Lines changed: 106 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,7 +1056,7 @@ func shouldSuppressPHPSubprocessLine(line string) bool {
10561056
func runPHPNewCommand(ctx context.Context, emit func(domain.Event) bool, opt phpNewOptions) error {
10571057
tracker := newStepTracker(emit)
10581058

1059-
entry, err := findPHPInstallerCLIEntry()
1059+
entry, err := findPHPSymfonyCLIEntry()
10601060
if err != nil {
10611061
tracker.FailRemaining()
10621062
return err
@@ -1901,7 +1901,7 @@ type systemStatusJSON struct {
19011901
}
19021902

19031903
func fetchSystemStatus(ctx context.Context) (domain.SystemStatus, error) {
1904-
entry, err := findPHPInstallerCLIEntry()
1904+
entry, err := findPHPSystemStatusEntry()
19051905
if err != nil {
19061906
return domain.SystemStatus{}, err
19071907
}
@@ -1961,12 +1961,89 @@ func fetchSystemStatus(ctx context.Context) (domain.SystemStatus, error) {
19611961
}
19621962

19631963
func findPHPInstallerCLIEntry() (string, error) {
1964+
// Backwards-compatible alias; keep for older callers.
1965+
return findPHPSymfonyCLIEntry()
1966+
}
1967+
1968+
func looksLikePHPScript(path string) bool {
1969+
f, err := os.Open(path)
1970+
if err != nil {
1971+
return false
1972+
}
1973+
defer f.Close()
1974+
1975+
buf := make([]byte, 256)
1976+
n, _ := f.Read(buf)
1977+
if n <= 0 {
1978+
return false
1979+
}
1980+
head := strings.ToLower(string(buf[:n]))
1981+
if strings.Contains(head, "<?php") {
1982+
return true
1983+
}
1984+
if strings.HasPrefix(head, "#!") && strings.Contains(head, "php") {
1985+
return true
1986+
}
1987+
return false
1988+
}
1989+
1990+
func findPHPSymfonyCLIEntry() (string, error) {
1991+
// This entrypoint must be the internal Symfony Console runner (installer/bin/evo),
1992+
// not the end-user bootstrapper (bin/evo). The bootstrapper proxies to the Go
1993+
// binary and will fail when we pass PHP-only flags like --no-ansi/--no-interaction.
1994+
19641995
candidates := []string{}
1996+
if exe, err := os.Executable(); err == nil && exe != "" {
1997+
exeDir := filepath.Dir(exe)
1998+
base := filepath.Dir(exeDir)
1999+
// Typical layout: <root>/bin/evo.bin and <root>/installer/bin/evo
2000+
candidates = append(candidates,
2001+
filepath.Join(base, "installer", "bin", "evo"),
2002+
filepath.Join(exeDir, "installer", "bin", "evo"),
2003+
filepath.Join(filepath.Dir(base), "installer", "bin", "evo"),
2004+
)
2005+
}
19652006

1966-
// Prefer the bootstrapper on PATH (installed alongside the Go binary).
1967-
if p, err := exec.LookPath("evo"); err == nil && p != "" {
1968-
candidates = append(candidates, p)
2007+
// Repo-local fallbacks (when running from source checkout).
2008+
candidates = append(candidates, filepath.Join("installer", "bin", "evo"))
2009+
2010+
for _, p := range candidates {
2011+
p = strings.TrimSpace(p)
2012+
if p == "" {
2013+
continue
2014+
}
2015+
fi, err := os.Stat(p)
2016+
if err != nil || fi.IsDir() {
2017+
continue
2018+
}
2019+
if !looksLikePHPScript(p) {
2020+
continue
2021+
}
2022+
if !looksLikeSymfonyEntry(p) {
2023+
continue
2024+
}
2025+
2026+
abs, absErr := filepath.Abs(p)
2027+
if absErr != nil {
2028+
return p, nil
2029+
}
2030+
return abs, nil
2031+
}
2032+
2033+
return "", fmt.Errorf("unable to find Symfony PHP CLI entry (expected installer/bin/evo). Ensure the PHP installer package files are present next to the Go binary")
2034+
}
2035+
2036+
func findPHPSystemStatusEntry() (string, error) {
2037+
// Prefer the Symfony entry (more complete), but fall back to the bootstrapper
2038+
// which implements `system-status` without requiring Composer deps.
2039+
if p, err := findPHPSymfonyCLIEntry(); err == nil {
2040+
return p, nil
19692041
}
2042+
return findPHPBootstrapperEntry()
2043+
}
2044+
2045+
func findPHPBootstrapperEntry() (string, error) {
2046+
candidates := []string{}
19702047

19712048
// Prefer a sibling `evo` script next to the running executable (common install layout:
19722049
// `evo` bootstrapper + `evo.bin` Go binary in the same directory).
@@ -1977,13 +2054,17 @@ func findPHPInstallerCLIEntry() (string, error) {
19772054
}
19782055
}
19792056

1980-
// Repo-local fallbacks (when running from source checkout).
1981-
candidates = append(candidates,
1982-
filepath.Join("installer", "bin", "evo"),
1983-
filepath.Join("bin", "evo"),
1984-
)
2057+
// Prefer the bootstrapper on PATH.
2058+
if p, err := exec.LookPath("evo"); err == nil && p != "" {
2059+
candidates = append(candidates, p)
2060+
}
2061+
2062+
// Repo-local fallback.
2063+
candidates = append(candidates, filepath.Join("bin", "evo"))
2064+
19852065
for _, p := range candidates {
1986-
if strings.TrimSpace(p) == "" {
2066+
p = strings.TrimSpace(p)
2067+
if p == "" {
19872068
continue
19882069
}
19892070
fi, err := os.Stat(p)
@@ -1993,36 +2074,38 @@ func findPHPInstallerCLIEntry() (string, error) {
19932074
if !looksLikePHPScript(p) {
19942075
continue
19952076
}
1996-
19972077
abs, absErr := filepath.Abs(p)
19982078
if absErr != nil {
19992079
return p, nil
20002080
}
20012081
return abs, nil
20022082
}
2003-
return "", fmt.Errorf("unable to find installer PHP CLI entry (tried: %s)", strings.Join(candidates, ", "))
2083+
2084+
return "", fmt.Errorf("unable to find PHP bootstrapper entry (tried: %s)", strings.Join(candidates, ", "))
20042085
}
20052086

2006-
func looksLikePHPScript(path string) bool {
2087+
func looksLikeSymfonyEntry(path string) bool {
2088+
// Fast path: expected location.
2089+
p := filepath.ToSlash(path)
2090+
if strings.HasSuffix(p, "/installer/bin/evo") {
2091+
return true
2092+
}
2093+
20072094
f, err := os.Open(path)
20082095
if err != nil {
20092096
return false
20102097
}
20112098
defer f.Close()
20122099

2013-
buf := make([]byte, 256)
2100+
buf := make([]byte, 2048)
20142101
n, _ := f.Read(buf)
20152102
if n <= 0 {
20162103
return false
20172104
}
2018-
head := strings.ToLower(string(buf[:n]))
2019-
if strings.Contains(head, "<?php") {
2020-
return true
2021-
}
2022-
if strings.HasPrefix(head, "#!") && strings.Contains(head, "php") {
2023-
return true
2024-
}
2025-
return false
2105+
head := string(buf[:n])
2106+
return strings.Contains(head, "EvolutionCMS\\\\Installer\\\\Application") ||
2107+
strings.Contains(head, "Internal PHP CLI entrypoint") ||
2108+
strings.Contains(head, "Symfony Console")
20262109
}
20272110

20282111
func detectExistingEvoInstall(dir string) (bool, string) {

internal/engine/mock/engine.go

Lines changed: 82 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,7 @@ type systemStatusJSON struct {
398398
}
399399

400400
func fetchSystemStatus(ctx context.Context) (domain.SystemStatus, error) {
401-
entry, err := findPHPInstallerCLIEntry()
401+
entry, err := findPHPSystemStatusEntry()
402402
if err != nil {
403403
return domain.SystemStatus{}, err
404404
}
@@ -458,28 +458,87 @@ func fetchSystemStatus(ctx context.Context) (domain.SystemStatus, error) {
458458
}
459459

460460
func findPHPInstallerCLIEntry() (string, error) {
461+
return findPHPSymfonyCLIEntry()
462+
}
463+
464+
func looksLikePHPScript(path string) bool {
465+
f, err := os.Open(path)
466+
if err != nil {
467+
return false
468+
}
469+
defer f.Close()
470+
471+
buf := make([]byte, 256)
472+
n, _ := f.Read(buf)
473+
if n <= 0 {
474+
return false
475+
}
476+
head := strings.ToLower(string(buf[:n]))
477+
if strings.Contains(head, "<?php") {
478+
return true
479+
}
480+
if strings.HasPrefix(head, "#!") && strings.Contains(head, "php") {
481+
return true
482+
}
483+
return false
484+
}
485+
486+
func findPHPSymfonyCLIEntry() (string, error) {
461487
candidates := []string{}
488+
if exe, err := os.Executable(); err == nil && exe != "" {
489+
exeDir := filepath.Dir(exe)
490+
base := filepath.Dir(exeDir)
491+
candidates = append(candidates,
492+
filepath.Join(base, "installer", "bin", "evo"),
493+
filepath.Join(exeDir, "installer", "bin", "evo"),
494+
filepath.Join(filepath.Dir(base), "installer", "bin", "evo"),
495+
)
496+
}
497+
candidates = append(candidates, filepath.Join("installer", "bin", "evo"))
462498

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)
499+
for _, p := range candidates {
500+
p = strings.TrimSpace(p)
501+
if p == "" {
502+
continue
503+
}
504+
fi, err := os.Stat(p)
505+
if err != nil || fi.IsDir() {
506+
continue
507+
}
508+
if !looksLikePHPScript(p) {
509+
continue
510+
}
511+
if !looksLikeSymfonyEntry(p) {
512+
continue
513+
}
514+
return p, nil
515+
}
516+
return "", fmt.Errorf("unable to find Symfony PHP CLI entry (expected installer/bin/evo). Ensure the PHP installer package files are present next to the Go binary")
517+
}
518+
519+
func findPHPSystemStatusEntry() (string, error) {
520+
if p, err := findPHPSymfonyCLIEntry(); err == nil {
521+
return p, nil
466522
}
523+
return findPHPBootstrapperEntry()
524+
}
467525

468-
// Prefer a sibling `evo` script next to the running executable (common install layout).
526+
func findPHPBootstrapperEntry() (string, error) {
527+
candidates := []string{}
469528
if exe, err := os.Executable(); err == nil && exe != "" {
470529
exeDir := filepath.Dir(exe)
471530
if exeDir != "" && exeDir != "." {
472531
candidates = append(candidates, filepath.Join(exeDir, "evo"))
473532
}
474533
}
534+
if p, err := exec.LookPath("evo"); err == nil && p != "" {
535+
candidates = append(candidates, p)
536+
}
537+
candidates = append(candidates, filepath.Join("bin", "evo"))
475538

476-
// Repo-local fallbacks (when running from source checkout).
477-
candidates = append(candidates,
478-
filepath.Join("installer", "bin", "evo"),
479-
filepath.Join("bin", "evo"),
480-
)
481539
for _, p := range candidates {
482-
if strings.TrimSpace(p) == "" {
540+
p = strings.TrimSpace(p)
541+
if p == "" {
483542
continue
484543
}
485544
fi, err := os.Stat(p)
@@ -491,27 +550,28 @@ func findPHPInstallerCLIEntry() (string, error) {
491550
}
492551
return p, nil
493552
}
494-
return "", fmt.Errorf("unable to find installer PHP CLI entry (tried: %s)", strings.Join(candidates, ", "))
553+
return "", fmt.Errorf("unable to find PHP bootstrapper entry (tried: %s)", strings.Join(candidates, ", "))
495554
}
496555

497-
func looksLikePHPScript(path string) bool {
556+
func looksLikeSymfonyEntry(path string) bool {
557+
p := filepath.ToSlash(path)
558+
if strings.HasSuffix(p, "/installer/bin/evo") {
559+
return true
560+
}
561+
498562
f, err := os.Open(path)
499563
if err != nil {
500564
return false
501565
}
502566
defer f.Close()
503567

504-
buf := make([]byte, 256)
568+
buf := make([]byte, 2048)
505569
n, _ := f.Read(buf)
506570
if n <= 0 {
507571
return false
508572
}
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
573+
head := string(buf[:n])
574+
return strings.Contains(head, "EvolutionCMS\\\\Installer\\\\Application") ||
575+
strings.Contains(head, "Internal PHP CLI entrypoint") ||
576+
strings.Contains(head, "Symfony Console")
517577
}

internal/ui/styles.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,21 @@ package ui
33
import "github.com/charmbracelet/lipgloss"
44

55
const progressFillHex = "#00D787"
6+
const brightBlue = "#3b82f6"
67

78
var (
89
panelBorder = lipgloss.RoundedBorder()
910
panelStyle = lipgloss.NewStyle().Border(panelBorder)
1011
panelTitleStyle = lipgloss.NewStyle().Bold(true)
1112

12-
activeStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("12"))
13+
activeStyle = lipgloss.NewStyle().Foreground(lipgloss.Color(brightBlue))
1314
okStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("10"))
1415
warnStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("11"))
1516
errStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("9"))
1617
mutedStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("8"))
1718

1819
logoStyle = lipgloss.NewStyle().Foreground(lipgloss.Color(progressFillHex))
19-
versionStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("12"))
20+
versionStyle = lipgloss.NewStyle().Foreground(lipgloss.Color(brightBlue))
2021
taglineStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("11"))
2122

2223
questionStyle = versionStyle

0 commit comments

Comments
 (0)