Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

Passed
Push — add-update-command ( 52996a...7d33d9 )
by Pedro
14:43
created

shouldOfferConstraintFix()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 25
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 12
nc 6
nop 0
dl 0
loc 25
rs 8.8333
c 0
b 0
f 0
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
use Composer\Semver\Semver;
9
10
class EnsureLaravelVersionStep extends Step
11
{
12
    private ?string $composerConstraint = null;
13
14
    private ?string $composerSection = null;
15
16
    private ?string $suggestedConstraint = null;
17
18
    private ?int $detectedMajor = null;
19
20
    public function title(): string
21
    {
22
        return 'Laravel 12 or newer';
23
    }
24
25
    public function run(): StepResult
26
    {
27
        $this->resetState();
28
29
        $prettyVersion = $this->context()->installedPackagePrettyVersion('laravel/framework') ?? app()->version();
0 ignored issues
show
introduced by
The method version() does not exist on Illuminate\Container\Container. Are you sure you never get this type here, but always one of the subclasses? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

29
        $prettyVersion = $this->context()->installedPackagePrettyVersion('laravel/framework') ?? app()->/** @scrutinizer ignore-call */ version();
Loading history...
30
        $major = $this->context()->packageMajorVersion('laravel/framework');
31
32
        if ($major === null && preg_match('/(\d+)/', $prettyVersion, $matches)) {
33
            $major = (int) $matches[1];
34
        }
35
36
        $this->detectedMajor = $major;
37
        $this->composerConstraint = $this->context()->composerRequirement('laravel/framework');
38
        $this->composerSection = $this->context()->composerRequirementSection('laravel/framework');
39
40
        if ($major !== null && $major >= 12) {
41
            return StepResult::success("Detected Laravel {$prettyVersion}.");
42
        }
43
44
        if ($this->shouldOfferConstraintFix()) {
45
            $details = [
46
                "Detected Laravel version: {$prettyVersion}",
47
                sprintf(
48
                    'composer.json constraint (%s): %s',
49
                    $this->composerSection,
50
                    $this->composerConstraint
51
                ),
52
                "Suggested constraint update: {$this->suggestedConstraint}",
53
            ];
54
55
            return StepResult::failure(
56
                'Upgrade Laravel to version 12 before continuing.',
57
                $details,
58
                [
59
                    'suggested_constraint' => $this->suggestedConstraint,
60
                    'composer_section' => $this->composerSection,
61
                ]
62
            );
63
        }
64
65
        return StepResult::failure(
66
            'Upgrade Laravel to version 12 before continuing.',
67
            [
68
                "Laravel 12 is allowed by your composer.json constraints, you just need to run composer update to get it.",
69
                "Detected Laravel version: {$prettyVersion}"
70
            ]
71
        );
72
    }
73
74
    public function canFix(StepResult $result): bool
75
    {
76
        return $result->status === StepStatus::Failed && $this->suggestedConstraint !== null && $this->composerSection !== null;
77
    }
78
79
    public function fixMessage(StepResult $result): string
80
    {
81
        return 'We can update composer.json to allow installing Laravel 12 automatically. Apply this change?';
82
    }
83
84
    public function fix(StepResult $result): StepResult
85
    {
86
        if ($this->suggestedConstraint === null || $this->composerSection === null) {
87
            return StepResult::skipped('No composer constraint update required.');
88
        }
89
90
        $section = $this->composerSection;
91
        $constraint = $this->suggestedConstraint;
92
93
        $updated = $this->context()->updateComposerJson(function (array &$composer) use ($section, $constraint) {
94
            $composer[$section] = $composer[$section] ?? [];
95
            $composer[$section]['laravel/framework'] = $constraint;
96
        });
97
98
        if (! $updated) {
99
            return StepResult::failure('Could not update composer.json automatically.');
100
        }
101
102
        return StepResult::success('Updated laravel/framework composer constraint to allow Laravel 12.');
103
    }
104
105
    private function resetState(): void
106
    {
107
        $this->composerConstraint = null;
108
        $this->composerSection = null;
109
        $this->suggestedConstraint = null;
110
        $this->detectedMajor = null;
111
    }
112
113
    private function shouldOfferConstraintFix(): bool
114
    {
115
        if ($this->composerConstraint === null || $this->detectedMajor === null) {
116
            return false;
117
        }
118
119
        if ($this->detectedMajor < 11) {
120
            return false;
121
        }
122
123
        if ($this->composerSection === null) {
124
            return false;
125
        }
126
127
        if (! preg_match('/\d+/', $this->composerConstraint)) {
128
            return false;
129
        }
130
131
        if ($this->constraintAllowsMajor($this->composerConstraint, 12)) {
132
            return false;
133
        }
134
135
        $this->suggestedConstraint = $this->buildSuggestedConstraint($this->composerConstraint);
136
137
        return $this->suggestedConstraint !== null;
138
    }
139
140
    private function constraintAllowsMajor(string $constraint, int $major): bool
141
    {
142
        $constraint = trim($constraint);
143
144
        if ($constraint === '') {
145
            return true;
146
        }
147
148
        try {
149
            if (Semver::satisfies(sprintf('%d.0.0', $major), $constraint)) {
150
                return true;
151
            }
152
153
            if (Semver::satisfies(sprintf('%d.999.999', $major), $constraint)) {
154
                return true;
155
            }
156
        } catch (\Throwable $exception) {
157
            return str_contains($constraint, (string) $major);
158
        }
159
160
        return false;
161
    }
162
163
    private function buildSuggestedConstraint(string $constraint): ?string
164
    {
165
        $normalized = trim(preg_replace('/\s+/', ' ', $constraint) ?? '');
166
        $normalized = preg_replace('/\s*\|\|\s*/', ' || ', $normalized ?? '') ?? '';
167
        $normalized = preg_replace('/\s*\|\s*/', ' || ', $normalized) ?? '';
168
169
        if ($normalized === '') {
170
            return '^12.0';
171
        }
172
173
        if ($this->constraintAllowsMajor($normalized, 12)) {
174
            return $normalized;
175
        }
176
177
        if (str_contains($normalized, '12')) {
178
            return $normalized;
179
        }
180
181
        return rtrim($normalized, ' ') . ' || ^12.0';
182
    }
183
}
184