Skip to content

Commit 7d33d98

Browse files
committed
wip
1 parent 52996a2 commit 7d33d98

17 files changed

+872
-109
lines changed

src/app/Console/Commands/Upgrade/Step.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ public function canFix(StepResult $result): bool
2727
return false;
2828
}
2929

30+
public function fixMessage(StepResult $result): string
31+
{
32+
return 'Apply automatic fix?';
33+
}
34+
3035
public function fix(StepResult $result): StepResult
3136
{
3237
return StepResult::skipped('No automatic fix available.');

src/app/Console/Commands/Upgrade/UpgradeCommand.php

Lines changed: 47 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Backpack\CRUD\app\Console\Commands\Traits\PrettyCommandOutput;
66
use Illuminate\Console\Command;
7+
use RuntimeException;
78

89
class UpgradeCommand extends Command
910
{
@@ -30,15 +31,22 @@ public function handle(): int
3031
$version = (string) $this->argument('version');
3132
$majorVersion = $this->extractMajorVersion($version);
3233

33-
$stepClasses = $this->resolveStepsForMajor($majorVersion);
34+
try {
35+
$config = $this->resolveConfigForMajor($majorVersion);
36+
} catch (RuntimeException $exception) {
37+
$this->errorBlock($exception->getMessage());
3438

35-
if (empty($stepClasses)) {
39+
return Command::INVALID;
40+
}
41+
42+
$stepClasses = $config->steps();
43+
if (empty($stepClasses)) {
3644
$this->errorBlock("No automated checks registered for Backpack v{$majorVersion}.");
3745

3846
return Command::INVALID;
3947
}
4048

41-
$context = new UpgradeContext($majorVersion);
49+
$context = new UpgradeContext($majorVersion, addons: $config->addons());
4250

4351
$this->infoBlock("Backpack v{$majorVersion} upgrade assistant", 'upgrade');
4452

@@ -66,7 +74,9 @@ public function handle(): int
6674
$this->printResultDetails($result);
6775

6876
if ($this->shouldOfferFix($step, $result)) {
69-
$applyFix = $this->confirm(' Apply automatic fix?', false);
77+
$question = trim($step->fixMessage($result));
78+
$question = $question !== '' ? $question : 'Apply automatic fix?';
79+
$applyFix = $this->confirm(' '.$question, false);
7080

7181
if ($applyFix) {
7282
$this->progressBlock('Applying automatic fix');
@@ -213,22 +223,40 @@ protected function outputFormat(): string
213223
return $format !== '' ? $format : 'cli';
214224
}
215225

216-
protected function resolveStepsForMajor(string $majorVersion): array
226+
protected function resolveConfigForMajor(string $majorVersion): UpgradeConfigInterface
217227
{
218-
return match ($majorVersion) {
219-
'7' => [
220-
v7\Steps\EnsureLaravelVersionStep::class,
221-
v7\Steps\EnsureBackpackCrudRequirementStep::class,
222-
v7\Steps\EnsureMinimumStabilityStep::class,
223-
v7\Steps\EnsureFirstPartyAddonsAreCompatibleStep::class,
224-
v7\Steps\CheckShowOperationComponentStep::class,
225-
v7\Steps\CheckOperationConfigFilesStep::class,
226-
v7\Steps\CheckThemeTablerConfigStep::class,
227-
v7\Steps\DetectDeprecatedWysiwygUsageStep::class,
228-
v7\Steps\DetectEditorAddonRequirementsStep::class,
229-
],
230-
default => [],
231-
};
228+
$configProviderClass = sprintf('%s\\v%s\\UpgradeCommandConfig', __NAMESPACE__, $majorVersion);
229+
230+
if (! class_exists($configProviderClass)) {
231+
throw new RuntimeException(sprintf(
232+
'Missing upgrade config provider for Backpack v%s. Please create %s.',
233+
$majorVersion,
234+
$configProviderClass
235+
));
236+
}
237+
238+
$provider = $this->laravel
239+
? $this->laravel->make($configProviderClass)
240+
: new $configProviderClass();
241+
242+
if (! $provider instanceof UpgradeConfigInterface) {
243+
throw new RuntimeException(sprintf(
244+
'Upgrade config provider [%s] must implement %s.',
245+
$configProviderClass,
246+
UpgradeConfigInterface::class
247+
));
248+
}
249+
250+
$steps = $provider->steps();
251+
252+
if (! is_array($steps)) {
253+
throw new RuntimeException(sprintf(
254+
'Upgrade config provider [%s] must return an array of step class names.',
255+
$configProviderClass
256+
));
257+
}
258+
259+
return $provider;
232260
}
233261

234262
protected function extractMajorVersion(string $version): string
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
namespace Backpack\CRUD\app\Console\Commands\Upgrade;
4+
5+
interface UpgradeConfigInterface
6+
{
7+
/**
8+
* @return array<class-string<\Backpack\CRUD\app\Console\Commands\Upgrade\Step>>
9+
*/
10+
public function steps(): array;
11+
12+
/**
13+
* @return array<string, string>
14+
*/
15+
public function addons(): array;
16+
17+
public static function backpackCrudRequirement(): string;
18+
}

src/app/Console/Commands/Upgrade/UpgradeContext.php

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,29 @@ class UpgradeContext
3030

3131
protected array $searchableExtensions = ['php'];
3232

33+
protected array $addons = [];
34+
3335
public function __construct(
3436
protected readonly string $targetVersion,
3537
?Filesystem $filesystem = null,
36-
?string $basePath = null
38+
?string $basePath = null,
39+
array $addons = []
3740
) {
3841
$this->files = $filesystem ?? new Filesystem();
3942
$this->basePath = $this->normalizePath($basePath ?? $this->defaultBasePath());
43+
$this->addons = $addons;
4044
}
4145

4246
public function targetVersion(): string
4347
{
4448
return $this->targetVersion;
4549
}
4650

51+
public function addons(): array
52+
{
53+
return $this->addons;
54+
}
55+
4756
public function basePath(string $path = ''): string
4857
{
4958
$path = ltrim($path, '/\\');
@@ -234,6 +243,28 @@ public function writeFile(string $relativePath, string $contents): bool
234243
}
235244
}
236245

246+
public function deleteFile(string $relativePath): bool
247+
{
248+
$fullPath = $this->basePath($relativePath);
249+
250+
try {
251+
if (! $this->files->exists($fullPath)) {
252+
return true;
253+
}
254+
255+
$this->files->delete($fullPath);
256+
unset($this->fileCache[$relativePath]);
257+
258+
if ($relativePath === 'composer.json') {
259+
$this->composerJson = [];
260+
}
261+
262+
return true;
263+
} catch (\Throwable $exception) {
264+
return false;
265+
}
266+
}
267+
237268
public function updateComposerJson(callable $callback): bool
238269
{
239270
$composer = $this->composerJson();
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
3+
namespace Backpack\CRUD\app\Console\Commands\Upgrade\v7\Concerns;
4+
5+
use Backpack\CRUD\app\Console\Commands\Upgrade\UpgradeContext;
6+
7+
/**
8+
* @mixin \Backpack\CRUD\app\Console\Commands\Upgrade\Step
9+
*/
10+
trait InteractsWithCrudControllers
11+
{
12+
/**
13+
* Filter a list of file paths to those that look like CrudControllers and optionally validate contents.
14+
*/
15+
protected function filterCrudControllerPaths(array $paths, ?callable $contentsValidator = null): array
16+
{
17+
if (empty($paths)) {
18+
return [];
19+
}
20+
21+
$filtered = [];
22+
23+
foreach ($paths as $path) {
24+
if (! $this->isCrudControllerPath($path)) {
25+
continue;
26+
}
27+
28+
$contents = $this->context()->readFile($path);
29+
30+
if ($contents === null) {
31+
continue;
32+
}
33+
34+
if ($contentsValidator !== null && $contentsValidator($contents, $path) !== true) {
35+
continue;
36+
}
37+
38+
$filtered[] = $path;
39+
}
40+
41+
return $filtered;
42+
}
43+
44+
/**
45+
* Build a short list of preview lines for the provided paths.
46+
*/
47+
protected function previewLines(array $paths, int $limit = 10, ?callable $formatter = null): array
48+
{
49+
if (empty($paths)) {
50+
return [];
51+
}
52+
53+
$formatter ??= static fn (string $path): string => "- {$path}";
54+
55+
$preview = array_slice($paths, 0, $limit);
56+
$details = array_map($formatter, $preview);
57+
58+
$remaining = count($paths) - count($preview);
59+
60+
if ($remaining > 0) {
61+
$details[] = sprintf('… %d more occurrence(s) omitted.', $remaining);
62+
}
63+
64+
return $details;
65+
}
66+
67+
protected function isCrudControllerPath(string $path): bool
68+
{
69+
return str_contains($path, 'CrudController');
70+
}
71+
72+
abstract protected function context(): UpgradeContext;
73+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
namespace Backpack\CRUD\app\Console\Commands\Upgrade\v7\Steps;
4+
5+
use Backpack\CRUD\app\Console\Commands\Upgrade\Step;
6+
use Backpack\CRUD\app\Console\Commands\Upgrade\StepResult;
7+
use Backpack\CRUD\app\Console\Commands\Upgrade\StepStatus;
8+
9+
class CheckListOperationViewPublishedStep extends Step
10+
{
11+
protected string $relativePath = 'resources/views/vendor/backpack/crud/list.blade.php';
12+
13+
public function title(): string
14+
{
15+
return 'Published list operation view';
16+
}
17+
18+
public function run(): StepResult
19+
{
20+
if (! $this->context()->fileExists($this->relativePath)) {
21+
return StepResult::success('List operation view is not published, package default will be used.');
22+
}
23+
24+
return StepResult::warning(
25+
'A published list.blade.php was found. The bundled view received significant updates; delete the published copy to use the latest Backpack version.',
26+
["Published view: {$this->relativePath}"]
27+
);
28+
}
29+
30+
public function canFix(StepResult $result): bool
31+
{
32+
return $result->status === StepStatus::Warning && $this->context()->fileExists($this->relativePath);
33+
}
34+
35+
public function fixMessage(StepResult $result): string
36+
{
37+
return 'We can delete the published list.blade.php so Backpack uses the updated bundled view. Proceed?';
38+
}
39+
40+
public function fix(StepResult $result): StepResult
41+
{
42+
if (! $this->context()->fileExists($this->relativePath)) {
43+
return StepResult::skipped('Published list.blade.php was already removed.');
44+
}
45+
46+
if (! $this->context()->deleteFile($this->relativePath)) {
47+
return StepResult::failure("Could not delete {$this->relativePath} automatically.");
48+
}
49+
50+
return StepResult::success('Removed the published list.blade.php so the package view is used.');
51+
}
52+
}

0 commit comments

Comments
 (0)