Skip to content

Commit 4851061

Browse files
committed
[FIX] Install process.
1 parent 0aec5fa commit 4851061

6 files changed

Lines changed: 108 additions & 45 deletions

File tree

README.md

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,6 @@
22

33
CLI tool for creating new Evolution CMS projects.
44

5-
[![Latest Stable Version](https://img.shields.io/packagist/v/evolution-cms/installer?label=version)](https://packagist.org/packages/evolution-cms/installer)
6-
[![CMS Evolution](https://img.shields.io/badge/CMS-Evolution-brightgreen.svg)](https://github.com/evolution-cms/evolution)
7-
![PHP version](https://img.shields.io/packagist/php-v/evolution-cms/installer)
8-
[![License](https://img.shields.io/packagist/l/evolution-cms/installer)](https://packagist.org/packages/evolution-cms/installer)
9-
[![Issues](https://img.shields.io/github/issues/evolution-cms/installer)](https://github.com/evolution-cms/installer/issues)
10-
[![Stars](https://img.shields.io/packagist/stars/evolution-cms/installer)](https://packagist.org/packages/evolution-cms/installer)
11-
[![Total Downloads](https://img.shields.io/packagist/dt/evolution-cms/installer)](https://packagist.org/packages/evolution-cms/installer)
12-
135
## Requirements
146

157
- PHP 8.3+
@@ -209,7 +201,8 @@ make install
209201
Other commands:
210202

211203
```bash
212-
go run ./cmd/evo doctor
204+
#
205+
# Note: `evo doctor` command has been removed.
213206
go run ./cmd/evo version
214207
go run ./cmd/evo install -f
215208
```

cmd/evo/main.go

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import (
1414

1515
"github.com/evolution-cms/installer/internal/domain"
1616
installengine "github.com/evolution-cms/installer/internal/engine/install"
17-
"github.com/evolution-cms/installer/internal/engine/mock"
1817
"github.com/evolution-cms/installer/internal/ui"
1918
)
2019

@@ -50,11 +49,6 @@ func run(ctx context.Context, args []string) int {
5049
return 1
5150
}
5251
return runInstall(ctx, args[1:])
53-
case "doctor":
54-
if !ensureComposer2(ctx) {
55-
return 1
56-
}
57-
return runTUI(ctx, ui.ModeDoctor, nil)
5852
default:
5953
if !ensureComposer2(ctx) {
6054
return 1
@@ -227,8 +221,6 @@ func runTUI(ctx context.Context, mode ui.Mode, installOpt *installengine.Options
227221
opt = *installOpt
228222
}
229223
engine = installengine.New(opt)
230-
} else {
231-
engine = mock.New()
232224
}
233225
engineCtx, cancel := context.WithCancel(ctx)
234226
defer cancel()
@@ -249,7 +241,6 @@ func printUsage() {
249241
fmt.Println("")
250242
fmt.Println("Usage:")
251243
fmt.Println(" evo install [dir] [flags] Run TUI installer")
252-
fmt.Println(" evo doctor Run TUI doctor (mock engine)")
253244
fmt.Println(" evo version Print version")
254245
fmt.Println("")
255246
fmt.Println("Common flags:")

internal/engine/install/engine.go

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1087,7 +1087,10 @@ func runPHPNewCommand(ctx context.Context, emit func(domain.Event) bool, opt php
10871087
args = append(args, "--force")
10881088
}
10891089

1090-
cmd := exec.CommandContext(ctx, "php", args...)
1090+
runCtx, cancel := context.WithCancel(ctx)
1091+
defer cancel()
1092+
1093+
cmd := exec.CommandContext(runCtx, "php", args...)
10911094
if strings.TrimSpace(opt.WorkDir) != "" {
10921095
cmd.Dir = opt.WorkDir
10931096
}
@@ -1143,6 +1146,11 @@ func runPHPNewCommand(ctx context.Context, emit func(domain.Event) bool, opt php
11431146
for l := range linesCh {
11441147
line := l.text
11451148
tracker.OnLine(line)
1149+
if tracker.HasFailed() {
1150+
// Abort immediately when a step is marked failed (e.g., download failed)
1151+
// even if the PHP command would otherwise continue.
1152+
cancel()
1153+
}
11461154
stepID := tracker.CurrentStepID()
11471155

11481156
if shouldSuppressPHPSubprocessLine(line) {
@@ -1217,6 +1225,10 @@ func runPHPNewCommand(ctx context.Context, emit func(domain.Event) bool, opt php
12171225
tracker.FailRemaining()
12181226
return err
12191227
}
1228+
if tracker.HasFailed() {
1229+
tracker.FailRemaining()
1230+
return fmt.Errorf("installation aborted due to failed step")
1231+
}
12201232
tracker.FinishRemaining()
12211233
return nil
12221234
}
@@ -1246,13 +1258,17 @@ type stepTracker struct {
12461258
current string
12471259

12481260
done map[string]bool
1261+
1262+
started map[string]bool
1263+
failed bool
12491264
}
12501265

12511266
func newStepTracker(emit func(domain.Event) bool) *stepTracker {
12521267
return &stepTracker{
12531268
emit: emit,
12541269
current: "download",
12551270
done: map[string]bool{},
1271+
started: map[string]bool{},
12561272
}
12571273
}
12581274

@@ -1263,6 +1279,8 @@ func (t *stepTracker) CurrentStepID() string {
12631279
return t.current
12641280
}
12651281

1282+
func (t *stepTracker) HasFailed() bool { return t.failed }
1283+
12661284
func (t *stepTracker) start(stepID, label string, index int) {
12671285
if t.done[stepID] {
12681286
return
@@ -1271,6 +1289,7 @@ func (t *stepTracker) start(stepID, label string, index int) {
12711289
return
12721290
}
12731291
t.current = stepID
1292+
t.started[stepID] = true
12741293
_ = t.emit(domain.Event{
12751294
Type: domain.EventStepStart,
12761295
StepID: stepID,
@@ -1289,6 +1308,9 @@ func (t *stepTracker) doneStep(stepID string, ok bool) {
12891308
return
12901309
}
12911310
t.done[stepID] = true
1311+
if !ok {
1312+
t.failed = true
1313+
}
12921314
sev := domain.SeverityInfo
12931315
if !ok {
12941316
sev = domain.SeverityError
@@ -1320,6 +1342,10 @@ func (t *stepTracker) OnLine(line string) {
13201342
t.doneStep("install", true)
13211343
t.doneStep("presets", true)
13221344
}
1345+
// Install command now reports migrations and composer install as part of Step 4.
1346+
if strings.Contains(line, "Running database migrations") || strings.Contains(line, "Running database seeders") {
1347+
t.start("install", "Step 4: Install Evolution CMS", 4)
1348+
}
13231349

13241350
// Step 6 marker: deps update starts after install + presets.
13251351
if strings.Contains(line, "Updating dependencies with Composer") {
@@ -1344,6 +1370,12 @@ func (t *stepTracker) OnLine(line string) {
13441370
if strings.Contains(strings.ToLower(line), "failed to download evolution cms") {
13451371
t.doneStep("download", false)
13461372
}
1373+
if strings.Contains(strings.ToLower(line), "migration failed") {
1374+
t.doneStep("install", false)
1375+
}
1376+
if strings.Contains(strings.ToLower(line), "failed to install dependencies") {
1377+
t.doneStep("install", false)
1378+
}
13471379
if strings.Contains(strings.ToLower(line), "failed to update dependencies") {
13481380
t.doneStep("dependencies", false)
13491381
}
@@ -1354,12 +1386,16 @@ func (t *stepTracker) FinishRemaining() {
13541386
if t.done["install"] {
13551387
t.doneStep("presets", true)
13561388
}
1357-
// If PHP ran to completion, mark remaining steps as done (best-effort).
1358-
t.doneStep("download", true)
1359-
t.doneStep("install", true)
1360-
t.doneStep("presets", true)
1361-
t.doneStep("dependencies", true)
1362-
t.doneStep("finalize", true)
1389+
// If PHP ran to completion, mark any started but not-done steps as OK (best-effort),
1390+
// but never override an explicit failure.
1391+
if t.failed {
1392+
return
1393+
}
1394+
for _, id := range []string{"download", "install", "dependencies", "finalize"} {
1395+
if t.started[id] && !t.done[id] {
1396+
t.doneStep(id, true)
1397+
}
1398+
}
13631399
}
13641400

13651401
func (t *stepTracker) FailRemaining() {

internal/ui/styles.go

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

55
const progressFillHex = "#00D787"
6-
const brightBlue = "#3b82f6"
6+
const brightBlue = "#60a5fa"
7+
const brightRed = "#ef4444"
8+
const brightYellow = "#fde047"
79

810
var (
911
panelBorder = lipgloss.RoundedBorder()
@@ -12,13 +14,13 @@ var (
1214

1315
activeStyle = lipgloss.NewStyle().Foreground(lipgloss.Color(brightBlue))
1416
okStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("10"))
15-
warnStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("11"))
16-
errStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("9"))
17+
warnStyle = lipgloss.NewStyle().Foreground(lipgloss.Color(brightYellow))
18+
errStyle = lipgloss.NewStyle().Foreground(lipgloss.Color(brightRed))
1719
mutedStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("8"))
1820

1921
logoStyle = lipgloss.NewStyle().Foreground(lipgloss.Color(progressFillHex))
2022
versionStyle = lipgloss.NewStyle().Foreground(lipgloss.Color(brightBlue))
21-
taglineStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("11"))
23+
taglineStyle = lipgloss.NewStyle().Foreground(lipgloss.Color(brightYellow))
2224

2325
questionStyle = versionStyle
2426
defaultInputStyle = mutedStyle

src/Commands/InstallCommand.php

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1296,8 +1296,13 @@ protected function setupComposer(string $projectPath): void
12961296
$composerCommand = $this->resolveComposerCommand($composerWorkDir);
12971297

12981298
try {
1299-
$process = $this->runComposer($composerCommand, ['install', '--no-dev', '--prefer-dist', '--no-scripts'], $composerWorkDir);
1300-
if ($process->isSuccessful()) {
1299+
$installArgs = ['install', '--no-dev', '--prefer-dist', '--no-scripts'];
1300+
$process = $this->runComposer($composerCommand, $installArgs, $composerWorkDir);
1301+
if ($process->isSuccessful() && !$this->isComposerVendorHealthy($composerWorkDir)) {
1302+
$this->tui->addLog('Composer install finished but vendor is incomplete. Retrying with --prefer-source (this can happen due to GitHub rate limits)...', 'warning');
1303+
$process = $this->runComposer($composerCommand, ['install', '--no-dev', '--prefer-source', '--no-scripts'], $composerWorkDir);
1304+
}
1305+
if ($process->isSuccessful() && $this->isComposerVendorHealthy($composerWorkDir)) {
13011306
$this->tui->addLog('Dependencies installed successfully.', 'success');
13021307
return;
13031308
}
@@ -1322,16 +1327,16 @@ protected function setupComposer(string $projectPath): void
13221327
}
13231328

13241329
// Reinstall with prefer-dist
1325-
$reinstall = $this->runComposer($composerCommand, ['install', '--no-dev', '--prefer-dist', '--no-scripts'], $composerWorkDir);
1326-
if ($reinstall->isSuccessful()) {
1330+
$reinstall = $this->runComposer($composerCommand, $installArgs, $composerWorkDir);
1331+
if ($reinstall->isSuccessful() && $this->isComposerVendorHealthy($composerWorkDir)) {
13271332
$this->tui->addLog('Dependencies reinstalled successfully.', 'success');
13281333
return;
13291334
}
13301335

13311336
// If install fails, try update as fallback
13321337
$this->tui->addLog('Install failed. Trying composer update...', 'warning');
13331338
$update = $this->runComposer($composerCommand, ['update', '--no-dev', '--prefer-dist', '--no-scripts'], $composerWorkDir);
1334-
if ($update->isSuccessful()) {
1339+
if ($update->isSuccessful() && $this->isComposerVendorHealthy($composerWorkDir)) {
13351340
$this->tui->addLog('Dependencies updated successfully.', 'success');
13361341
return;
13371342
}
@@ -1345,7 +1350,7 @@ protected function setupComposer(string $projectPath): void
13451350
|| str_contains($fullOutput, 'requires php >=8.4')) {
13461351
$this->tui->addLog('composer.lock is not compatible with current PHP. Running composer update...', 'warning');
13471352
$update = $this->runComposer($composerCommand, ['update', '--no-dev', '--prefer-dist', '--no-scripts'], $composerWorkDir);
1348-
if ($update->isSuccessful()) {
1353+
if ($update->isSuccessful() && $this->isComposerVendorHealthy($composerWorkDir)) {
13491354
$this->tui->addLog('Dependencies updated successfully.', 'success');
13501355
return;
13511356
}
@@ -1369,8 +1374,8 @@ protected function setupComposer(string $projectPath): void
13691374
}
13701375

13711376
// Reinstall with prefer-dist
1372-
$reinstall = $this->runComposer($composerCommand, ['install', '--no-dev', '--prefer-dist', '--no-scripts'], $composerWorkDir);
1373-
if ($reinstall->isSuccessful()) {
1377+
$reinstall = $this->runComposer($composerCommand, $installArgs, $composerWorkDir);
1378+
if ($reinstall->isSuccessful() && $this->isComposerVendorHealthy($composerWorkDir)) {
13741379
$this->tui->addLog('Dependencies reinstalled successfully.', 'success');
13751380
return;
13761381
}
@@ -1389,6 +1394,22 @@ protected function setupComposer(string $projectPath): void
13891394
}
13901395
}
13911396

1397+
protected function isComposerVendorHealthy(string $composerWorkDir): bool
1398+
{
1399+
$autoload = $composerWorkDir . '/vendor/autoload.php';
1400+
if (!is_file($autoload)) {
1401+
return false;
1402+
}
1403+
1404+
// Migrations/seeders use Artisan which requires Symfony Console.
1405+
$symfonyConsole = $composerWorkDir . '/vendor/symfony/console/Application.php';
1406+
if (!is_file($symfonyConsole)) {
1407+
return false;
1408+
}
1409+
1410+
return true;
1411+
}
1412+
13921413
protected function runComposer(array $composerCommand, array $args, string $workingDir): Process
13931414
{
13941415
$process = new Process([

src/Commands/NewCommand.php

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1287,8 +1287,13 @@ protected function setupComposer(string $projectPath): void
12871287
$composerCommand = $this->resolveComposerCommand($composerWorkDir);
12881288

12891289
try {
1290-
$process = $this->runComposer($composerCommand, ['install', '--no-dev', '--prefer-dist', '--no-scripts'], $composerWorkDir);
1291-
if ($process->isSuccessful()) {
1290+
$installArgs = ['install', '--no-dev', '--prefer-dist', '--no-scripts'];
1291+
$process = $this->runComposer($composerCommand, $installArgs, $composerWorkDir);
1292+
if ($process->isSuccessful() && !$this->isComposerVendorHealthy($composerWorkDir)) {
1293+
$this->tui->addLog('Composer install finished but vendor is incomplete. Retrying with --prefer-source (this can happen due to GitHub rate limits)...', 'warning');
1294+
$process = $this->runComposer($composerCommand, ['install', '--no-dev', '--prefer-source', '--no-scripts'], $composerWorkDir);
1295+
}
1296+
if ($process->isSuccessful() && $this->isComposerVendorHealthy($composerWorkDir)) {
12921297
$this->tui->addLog('Dependencies installed successfully.', 'success');
12931298
return;
12941299
}
@@ -1313,16 +1318,16 @@ protected function setupComposer(string $projectPath): void
13131318
}
13141319

13151320
// Reinstall with prefer-dist
1316-
$reinstall = $this->runComposer($composerCommand, ['install', '--no-dev', '--prefer-dist', '--no-scripts'], $composerWorkDir);
1317-
if ($reinstall->isSuccessful()) {
1321+
$reinstall = $this->runComposer($composerCommand, $installArgs, $composerWorkDir);
1322+
if ($reinstall->isSuccessful() && $this->isComposerVendorHealthy($composerWorkDir)) {
13181323
$this->tui->addLog('Dependencies reinstalled successfully.', 'success');
13191324
return;
13201325
}
13211326

13221327
// If install fails, try update as fallback
13231328
$this->tui->addLog('Install failed. Trying composer update...', 'warning');
13241329
$update = $this->runComposer($composerCommand, ['update', '--no-dev', '--prefer-dist', '--no-scripts'], $composerWorkDir);
1325-
if ($update->isSuccessful()) {
1330+
if ($update->isSuccessful() && $this->isComposerVendorHealthy($composerWorkDir)) {
13261331
$this->tui->addLog('Dependencies updated successfully.', 'success');
13271332
return;
13281333
}
@@ -1336,7 +1341,7 @@ protected function setupComposer(string $projectPath): void
13361341
|| str_contains($fullOutput, 'requires php >=8.4')) {
13371342
$this->tui->addLog('composer.lock is not compatible with current PHP. Running composer update...', 'warning');
13381343
$update = $this->runComposer($composerCommand, ['update', '--no-dev', '--prefer-dist', '--no-scripts'], $composerWorkDir);
1339-
if ($update->isSuccessful()) {
1344+
if ($update->isSuccessful() && $this->isComposerVendorHealthy($composerWorkDir)) {
13401345
$this->tui->addLog('Dependencies updated successfully.', 'success');
13411346
return;
13421347
}
@@ -1360,8 +1365,8 @@ protected function setupComposer(string $projectPath): void
13601365
}
13611366

13621367
// Reinstall with prefer-dist
1363-
$reinstall = $this->runComposer($composerCommand, ['install', '--no-dev', '--prefer-dist', '--no-scripts'], $composerWorkDir);
1364-
if ($reinstall->isSuccessful()) {
1368+
$reinstall = $this->runComposer($composerCommand, $installArgs, $composerWorkDir);
1369+
if ($reinstall->isSuccessful() && $this->isComposerVendorHealthy($composerWorkDir)) {
13651370
$this->tui->addLog('Dependencies reinstalled successfully.', 'success');
13661371
return;
13671372
}
@@ -1380,6 +1385,21 @@ protected function setupComposer(string $projectPath): void
13801385
}
13811386
}
13821387

1388+
protected function isComposerVendorHealthy(string $composerWorkDir): bool
1389+
{
1390+
$autoload = $composerWorkDir . '/vendor/autoload.php';
1391+
if (!is_file($autoload)) {
1392+
return false;
1393+
}
1394+
1395+
$symfonyConsole = $composerWorkDir . '/vendor/symfony/console/Application.php';
1396+
if (!is_file($symfonyConsole)) {
1397+
return false;
1398+
}
1399+
1400+
return true;
1401+
}
1402+
13831403
protected function runComposer(array $composerCommand, array $args, string $workingDir): Process
13841404
{
13851405
$process = new Process([

0 commit comments

Comments
 (0)