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 — main ( 9a5531...79449a )
by Pedro
12:50 queued 11:45
created

Install::installTheme()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 46
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 6
eloc 27
nc 8
nop 0
dl 0
loc 46
rs 8.8657
c 1
b 1
f 0
1
<?php
2
3
namespace Backpack\CRUD\app\Console\Commands;
4
5
use Backpack\CRUD\BackpackServiceProvider;
6
use Carbon\Carbon;
7
use Illuminate\Console\Command;
8
use Illuminate\Support\Facades\Hash;
9
use Illuminate\Support\Str;
10
use Symfony\Component\Console\Output\OutputInterface;
11
use Symfony\Component\Process\Process;
12
13
class Install extends Command
14
{
15
    use Traits\PrettyCommandOutput;
16
17
    protected $progressBar;
18
19
    /**
20
     * The name and signature of the console command.
21
     *
22
     * @var string
23
     */
24
    protected $signature = 'backpack:install
25
                                {--timeout=300} : How many seconds to allow each process to run.
26
                                {--debug} : Show process output or not. Useful for debugging.';
27
28
    /**
29
     * The console command description.
30
     *
31
     * @var string
32
     */
33
    protected $description = 'Install Backpack requirements on dev, publish files and create uploads directory.';
34
35
    /**
36
     * Addons variable.
37
     *
38
     * @var array
39
     */
40
    protected $addons = [
41
        Addons\RequirePro::class,
42
        Addons\RequireDevTools::class,
43
        Addons\RequireEditableColumns::class,
44
    ];
45
46
    /**
47
     * Themes variable.
48
     *
49
     * @var array
50
     */
51
    protected $themes = [
52
        Themes\RequireThemeCoreuiv2::class,
53
        Themes\RequireThemeCoreuiv4::class,
54
        Themes\RequireThemeTabler::class,
55
    ];
56
57
    /**
58
     * Execute the console command.
59
     *
60
     * @return mixed Command-line output
61
     */
62
    public function handle()
63
    {
64
        $this->infoBlock('Installing Backpack CRUD.', 'Step 1');
65
66
        // Publish files
67
        $this->progressBlock('Publishing configs, views, routes and assets');
68
        $this->executeArtisanProcess('vendor:publish', [
69
            '--provider' => BackpackServiceProvider::class,
70
            '--tag' => 'minimum',
71
        ]);
72
        $this->closeProgressBlock();
73
74
        // Create users table
75
        $this->progressBlock('Creating users table');
76
        $this->executeArtisanProcess('migrate', $this->option('no-interaction') ? ['--no-interaction' => true] : []);
77
        $this->closeProgressBlock();
78
79
        // Create CheckIfAdmin middleware
80
        $this->progressBlock('Creating CheckIfAdmin middleware');
81
        $this->executeArtisanProcess('backpack:publish-middleware');
82
        $this->closeProgressBlock();
83
84
        // Install Backpack Generators
85
        $this->progressBlock('Installing Generators');
86
        if (! file_exists('vendor/backpack/generators/composer.json')) {
87
            // only do this if Generators aren't already required
88
            $process = new Process(['composer', 'require', '--dev', 'backpack/generators']);
89
            $process->setTimeout(300);
90
            $process->run();
91
        }
92
        $this->closeProgressBlock();
93
94
        // Install Backpack Basset
95
        $this->progressBlock('Installing Basset');
96
        $this->executeArtisanProcess('basset:install --no-interaction');
97
        $this->closeProgressBlock();
98
99
        // Optional commands
100
        if (! $this->option('no-interaction')) {
101
            // Themes
102
            $this->installTheme();
103
104
            // Create users
105
            $this->createUsers();
106
107
            // Addons
108
            $this->installAddons();
109
        } elseif (! $this->isAnyThemeInstalled()) {
110
            // Install default theme
111
            $this->progressBlock('Installing default theme');
112
            $this->executeArtisanProcess('backpack:require:theme-coreuiv2');
113
            $this->closeProgressBlock();
114
        }
115
116
        // Done
117
        $url = Str::of(config('app.url'))->finish('/')->append('admin/');
118
        $this->infoBlock('Backpack installation complete.', 'done');
119
        $this->note("Go to <fg=blue>$url</> to access your new admin panel.");
120
        $this->note('You may need to run <fg=blue>php artisan serve</> to serve your Laravel project.');
121
        $this->newLine();
122
    }
123
124
    private function createUsers()
125
    {
126
        $userClass = config('backpack.base.user_model_fqn', 'App\Models\User');
127
        $userModel = new $userClass();
128
129
        $this->newLine();
130
        $this->infoBlock('Creating an admin.', 'Step 3');
131
        $this->note('Quickly jump in your admin panel, using the email & password you choose here.');
132
133
        // Test User Model DB Connection
134
        try {
135
            $userModel->getConnection()->getPdo();
136
        } catch (\Throwable $e) {
137
            $this->note('Error accessing the database, make sure the User Model has a valid DB connection.', 'red');
138
139
            return;
140
        }
141
142
        // Count current users
143
        $currentUsers = $userModel->count();
144
        $this->note(sprintf('Currently there %s in the <fg=blue>%s</> table.', trans_choice("{0} are <fg=blue>no users</>|{1} is <fg=blue>1 user</>|[2,*] are <fg=blue>$currentUsers users</>", $currentUsers), $userModel->getTable()));
145
146
        if ($currentUsers) {
147
            $this->note('Skipping creating an admin user.');
148
            $this->newLine();
149
150
            return;
151
        }
152
153
        $total = 0;
154
        while ($this->confirm(' Add '.($total ? 'another' : 'an').' admin user?')) {
155
            $name = $this->ask(' Name');
156
            $mail = $this->ask(" {$name}'s email");
157
            $pass = $this->secret(" {$name}'s password");
158
159
            try {
160
                $user = collect([
0 ignored issues
show
Bug introduced by
array('name' => $name, '...ades\Hash::make($pass)) of type array<string,mixed|string> is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $value of collect(). ( Ignorable by Annotation )

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

160
                $user = collect(/** @scrutinizer ignore-type */ [
Loading history...
161
                    'name' => $name,
162
                    'email' => $mail,
163
                    'password' => Hash::make($pass),
164
                ]);
165
166
                // Merge timestamps
167
                if ($userModel->timestamps) {
168
                    $user = $user->merge([
0 ignored issues
show
Bug introduced by
array('created_at' => Ca...> Carbon\Carbon::now()) of type array<string,Carbon\Carbon> is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $items of Illuminate\Support\Collection::merge(). ( Ignorable by Annotation )

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

168
                    $user = $user->merge(/** @scrutinizer ignore-type */ [
Loading history...
169
                        'created_at' => Carbon::now(),
170
                        'updated_at' => Carbon::now(),
171
                    ]);
172
                }
173
174
                $userModel->insert($user->toArray());
175
176
                $this->deleteLines(12);
177
                $this->progressBlock('Adding admin user');
178
                $this->closeProgressBlock();
179
                $this->note($name);
180
                $this->note($mail);
181
182
                $total++;
183
            } catch (\Throwable$e) {
184
                $this->errorBlock($e->getMessage());
185
            }
186
        }
187
188
        $this->deleteLines(3);
189
190
        if (! $total) {
191
            $this->deleteLines();
192
            $this->note('Skipping creating an admin user.');
193
            $this->newLine();
194
        }
195
    }
196
197
    private function isEveryAddonInstalled()
198
    {
199
        return collect($this->addons)->every(function ($addon) {
0 ignored issues
show
Bug introduced by
$this->addons of type array is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $value of collect(). ( Ignorable by Annotation )

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

199
        return collect(/** @scrutinizer ignore-type */ $this->addons)->every(function ($addon) {
Loading history...
200
            return file_exists($addon->path);
201
        });
202
    }
203
204
    private function updateAddonsStatus()
205
    {
206
        $this->addons = $this->addons->each(function (&$addon) {
207
            $isInstalled = file_exists($addon->path);
208
            $addon->status = $isInstalled ? 'installed' : 'not installed';
209
            $addon->statusColor = $isInstalled ? 'green' : 'yellow';
210
        });
211
    }
212
213
    private function installAddons()
214
    {
215
        // map the addons
216
        $this->addons = collect($this->addons)
0 ignored issues
show
Bug introduced by
$this->addons of type array is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $value of collect(). ( Ignorable by Annotation )

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

216
        $this->addons = collect(/** @scrutinizer ignore-type */ $this->addons)
Loading history...
Documentation Bug introduced by
It seems like collect($this->addons)->...ion(...) { /* ... */ }) of type Illuminate\Support\Collection is incompatible with the declared type array of property $addons.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
217
            ->map(function ($class) {
218
                return (object) $class::$addon;
219
            });
220
221
        // set addons current status (installed / not installed)
222
        $this->updateAddonsStatus();
223
224
        // if all addons are installed do nothing
225
        if ($this->isEveryAddonInstalled()) {
226
            return;
227
        }
228
229
        $this->infoBlock('Installing premium Backpack add-ons:', 'Step 4');
230
        $this->note('Add tons of features and functionality to your admin panel, using our paid add-ons.');
231
        $this->note('For more information, payment and access please visit <fg=blue>https://backpackforlaravel.com/pricing</>');
232
        $this->newLine();
233
234
        // Calculate the printed line count
235
        $printedLines = $this->addons
236
            ->map(function ($e) {
237
                return count($e->description);
238
            })
239
            ->reduce(function ($sum, $item) {
240
                return $sum + $item + 2;
241
            }, 0);
0 ignored issues
show
Bug introduced by
0 of type integer is incompatible with the type Illuminate\Support\Traits\TReduceInitial expected by parameter $initial of Illuminate\Support\Collection::reduce(). ( Ignorable by Annotation )

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

241
            }, /** @scrutinizer ignore-type */ 0);
Loading history...
242
243
        $total = 0;
244
        while (! $this->isEveryAddonInstalled()) {
245
            $input = (int) $this->listChoice('Would you like to install a premium Backpack add-on? <fg=gray>(enter option number: 1, 2 or 3)</>', $this->addons->toArray());
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->listChoice('Would...his->addons->toArray()) targeting Backpack\CRUD\app\Consol...s\Install::listChoice() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
246
247
            if ($input < 1 || $input > $this->addons->count()) {
248
                $this->deleteLines(3);
249
                break;
250
            }
251
252
            // Clear list
253
            $this->deleteLines($printedLines + 4 + ($total ? 2 : 0));
254
255
            try {
256
                $addon = $this->addons[$input - 1];
257
258
                // Install addon (low verbose level)
259
                $currentVerbosity = $this->output->getVerbosity();
260
                $this->output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE);
261
                $this->call($addon->command);
262
                $this->output->setVerbosity($currentVerbosity);
263
264
                // refresh list
265
                $this->updateAddonsStatus();
266
267
                $total++;
268
            } catch (\Throwable $e) {
269
                $this->errorBlock($e->getMessage());
270
            }
271
272
            $this->line('  ──────────', 'fg=gray');
273
            $this->newLine();
274
        }
275
    }
276
277
    private function isEveryThemeInstalled()
278
    {
279
        return $this->themes()->every(function ($theme) {
280
            return $theme->status == 'installed';
281
        });
282
    }
283
284
    private function isAnyThemeInstalled()
285
    {
286
        return $this->themes()->filter(function ($theme) {
287
            return $theme->status == 'installed';
288
        })->count() > 0;
289
    }
290
291
    private function installTheme()
292
    {
293
        // if all themes are installed do nothing
294
        if ($this->isEveryThemeInstalled()) {
295
            return;
296
        }
297
298
        $this->infoBlock('Installing a Theme.', 'Step 2');
299
        $this->note('Choose your admin UI, depending on your project and preferences.');
300
        $this->newLine();
301
302
        // Calculate the printed line count
303
        $printedLines = $this->themes()
304
            ->map(function ($e) {
305
                return count($e->description);
306
            })
307
            ->reduce(function ($sum, $item) {
308
                return $sum + $item + 2;
309
            }, 0);
0 ignored issues
show
Bug introduced by
0 of type integer is incompatible with the type Illuminate\Support\Traits\TReduceInitial expected by parameter $initial of Illuminate\Support\Collection::reduce(). ( Ignorable by Annotation )

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

309
            }, /** @scrutinizer ignore-type */ 0);
Loading history...
310
311
        $total = 0;
312
        $input = (int) $this->listChoice('Which Backpack theme would you like to install? <fg=gray>(enter option number: 1, 2 or 3)</>', $this->themes()->toArray());
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->listChoice('Which...s->themes()->toArray()) targeting Backpack\CRUD\app\Consol...s\Install::listChoice() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
313
314
        if ($input < 1 || $input > $this->themes()->count()) {
315
            $this->deleteLines(3);
316
            $this->note('Skipping installing a theme.');
317
            $this->newLine();
318
319
            return;
320
        }
321
322
        // Clear list
323
        $this->deleteLines($printedLines + 4 + ($total ? 2 : 0));
324
325
        try {
326
            $addon = $this->themes()[$input - 1];
327
328
            // Install addon (low verbose level)
329
            $currentVerbosity = $this->output->getVerbosity();
330
            $this->output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE);
331
            $this->call($addon->command);
332
            $this->output->setVerbosity($currentVerbosity);
333
334
            $total++;
335
        } catch (\Throwable $e) {
336
            $this->errorBlock($e->getMessage());
337
        }
338
    }
339
340
    public function themes()
341
    {
342
        return collect($this->themes)
0 ignored issues
show
Bug introduced by
$this->themes of type array is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $value of collect(). ( Ignorable by Annotation )

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

342
        return collect(/** @scrutinizer ignore-type */ $this->themes)
Loading history...
343
            ->map(function ($class) {
344
                return (object) $class::$addon;
345
            })->each(function (&$theme) {
346
                $isInstalled = file_exists($theme->path);
347
                $theme->status = $isInstalled ? 'installed' : 'not installed';
348
                $theme->statusColor = $isInstalled ? 'green' : 'yellow';
349
            });
350
    }
351
}
352