Subversion-Projekte lars-tiefland.laravel_shop

Revision

Revision 399 | Zur aktuellen Revision | Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
namespace Laravel\Sail\Console;
4
 
5
use Illuminate\Console\Command;
6
use RuntimeException;
7
use Symfony\Component\Process\Process;
8
 
9
class InstallCommand extends Command
10
{
11
    /**
12
     * The name and signature of the console command.
13
     *
14
     * @var string
15
     */
16
    protected $signature = 'sail:install
17
                {--with= : The services that should be included in the installation}
18
                {--devcontainer : Create a .devcontainer configuration directory}';
19
 
20
    /**
21
     * The console command description.
22
     *
23
     * @var string
24
     */
25
    protected $description = 'Install Laravel Sail\'s default Docker Compose file';
26
 
27
    /**
28
     * The available services that may be installed.
29
     *
30
     * @var array<string>
31
     */
32
    protected $services = [
33
        'mysql',
34
        'pgsql',
35
        'mariadb',
36
        'redis',
37
        'memcached',
38
        'meilisearch',
39
        'minio',
40
        'mailhog',
41
        'selenium',
42
    ];
43
 
44
    /**
45
     * Execute the console command.
46
     *
47
     * @return int|null
48
     */
49
    public function handle()
50
    {
51
        if ($this->option('with')) {
52
            $services = $this->option('with') == 'none' ? [] : explode(',', $this->option('with'));
53
        } elseif ($this->option('no-interaction')) {
54
            $services = ['mysql', 'redis', 'selenium', 'mailhog'];
55
        } else {
56
            $services = $this->gatherServicesWithSymfonyMenu();
57
        }
58
 
59
        if ($invalidServices = array_diff($services, $this->services)) {
60
            $this->error('Invalid services ['.implode(',', $invalidServices).'].');
61
 
62
            return 1;
63
        }
64
 
65
        $this->buildDockerCompose($services);
66
        $this->replaceEnvVariables($services);
67
        $this->configurePhpUnit();
68
 
69
        if ($this->option('devcontainer')) {
70
            $this->installDevContainer();
71
        }
72
 
73
        $this->info('Sail scaffolding installed successfully.');
74
 
75
        $this->prepareInstallation($services);
76
    }
77
 
78
    /**
79
     * Gather the desired Sail services using a Symfony menu.
80
     *
81
     * @return array
82
     */
83
    protected function gatherServicesWithSymfonyMenu()
84
    {
85
        return $this->choice('Which services would you like to install?', $this->services, 0, null, true);
86
    }
87
 
88
    /**
89
     * Build the Docker Compose file.
90
     *
91
     * @param  array  $services
92
     * @return void
93
     */
94
    protected function buildDockerCompose(array $services)
95
    {
96
        $depends = collect($services)
97
            ->map(function ($service) {
98
                return "            - {$service}";
99
            })->whenNotEmpty(function ($collection) {
100
                return $collection->prepend('depends_on:');
101
            })->implode("\n");
102
 
103
        $stubs = rtrim(collect($services)->map(function ($service) {
104
            return file_get_contents(__DIR__ . "/../../stubs/{$service}.stub");
105
        })->implode(''));
106
 
107
        $volumes = collect($services)
108
            ->filter(function ($service) {
109
                return in_array($service, ['mysql', 'pgsql', 'mariadb', 'redis', 'meilisearch', 'minio']);
110
            })->map(function ($service) {
111
                return "    sail-{$service}:\n        driver: local";
112
            })->whenNotEmpty(function ($collection) {
113
                return $collection->prepend('volumes:');
114
            })->implode("\n");
115
 
116
        $dockerCompose = file_get_contents(__DIR__ . '/../../stubs/docker-compose.stub');
117
 
118
        $dockerCompose = str_replace('{{depends}}', empty($depends) ? '' : '        '.$depends, $dockerCompose);
119
        $dockerCompose = str_replace('{{services}}', $stubs, $dockerCompose);
120
        $dockerCompose = str_replace('{{volumes}}', $volumes, $dockerCompose);
121
 
122
        // Replace Selenium with ARM base container on Apple Silicon...
123
        if (in_array('selenium', $services) && php_uname('m') === 'arm64') {
124
            $dockerCompose = str_replace('selenium/standalone-chrome', 'seleniarm/standalone-chromium', $dockerCompose);
125
        }
126
 
127
        // Remove empty lines...
128
        $dockerCompose = preg_replace("/(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+/", "\n", $dockerCompose);
129
 
130
        file_put_contents($this->laravel->basePath('docker-compose.yml'), $dockerCompose);
131
    }
132
 
133
    /**
134
     * Replace the Host environment variables in the app's .env file.
135
     *
136
     * @param  array  $services
137
     * @return void
138
     */
139
    protected function replaceEnvVariables(array $services)
140
    {
141
        $environment = file_get_contents($this->laravel->basePath('.env'));
142
 
143
        if (in_array('pgsql', $services)) {
144
            $environment = str_replace('DB_CONNECTION=mysql', "DB_CONNECTION=pgsql", $environment);
145
            $environment = str_replace('DB_HOST=127.0.0.1', "DB_HOST=pgsql", $environment);
146
            $environment = str_replace('DB_PORT=3306', "DB_PORT=5432", $environment);
147
        } elseif (in_array('mariadb', $services)) {
148
            $environment = str_replace('DB_HOST=127.0.0.1', "DB_HOST=mariadb", $environment);
149
        } else {
150
            $environment = str_replace('DB_HOST=127.0.0.1', "DB_HOST=mysql", $environment);
151
        }
152
 
153
        $environment = str_replace('DB_USERNAME=root', "DB_USERNAME=sail", $environment);
154
        $environment = preg_replace("/DB_PASSWORD=(.*)/", "DB_PASSWORD=password", $environment);
155
 
156
        $environment = str_replace('MEMCACHED_HOST=127.0.0.1', 'MEMCACHED_HOST=memcached', $environment);
157
        $environment = str_replace('REDIS_HOST=127.0.0.1', 'REDIS_HOST=redis', $environment);
158
 
159
        if (in_array('meilisearch', $services)) {
160
            $environment .= "\nSCOUT_DRIVER=meilisearch";
161
            $environment .= "\nMEILISEARCH_HOST=http://meilisearch:7700\n";
162
        }
163
 
164
        file_put_contents($this->laravel->basePath('.env'), $environment);
165
    }
166
 
167
    /**
168
     * Configure PHPUnit to use the dedicated testing database.
169
     *
170
     * @return void
171
     */
172
    protected function configurePhpUnit()
173
    {
174
        if (! file_exists($path = $this->laravel->basePath('phpunit.xml'))) {
175
            $path = $this->laravel->basePath('phpunit.xml.dist');
176
        }
177
 
178
        $phpunit = file_get_contents($path);
179
 
180
        $phpunit = preg_replace('/^.*DB_CONNECTION.*\n/m', '', $phpunit);
181
        $phpunit = str_replace('<!-- <env name="DB_DATABASE" value=":memory:"/> -->', '<env name="DB_DATABASE" value="testing"/>', $phpunit);
182
 
183
        file_put_contents($this->laravel->basePath('phpunit.xml'), $phpunit);
184
    }
185
 
186
    /**
187
     * Install the devcontainer.json configuration file.
188
     *
189
     * @return void
190
     */
191
    protected function installDevContainer()
192
    {
193
        if (! is_dir($this->laravel->basePath('.devcontainer'))) {
194
            mkdir($this->laravel->basePath('.devcontainer'), 0755, true);
195
        }
196
 
197
        file_put_contents(
198
            $this->laravel->basePath('.devcontainer/devcontainer.json'),
199
            file_get_contents(__DIR__.'/../../stubs/devcontainer.stub')
200
        );
201
 
202
        $environment = file_get_contents($this->laravel->basePath('.env'));
203
 
204
        $environment .= "\nWWWGROUP=1000";
205
        $environment .= "\nWWWUSER=1000\n";
206
 
207
        file_put_contents($this->laravel->basePath('.env'), $environment);
208
    }
209
 
210
    /**
211
     * Prepare the installation by pulling and building any necessary images.
212
     *
213
     * @param  array  $services
214
     * @return void
215
     */
216
    protected function prepareInstallation($services)
217
    {
218
        // Ensure docker is installed...
219
        if ($this->runCommands(['docker info > /dev/null 2>&1']) !== 0) {
220
            return;
221
        }
222
 
223
        if (count($services) > 0) {
224
            $status = $this->runCommands([
225
                './vendor/bin/sail pull '.implode(' ', $services),
226
            ]);
227
 
228
            if ($status === 0) {
229
                $this->info('Sail images installed successfully.');
230
            }
231
        }
232
 
233
        $status = $this->runCommands([
234
            './vendor/bin/sail build',
235
        ]);
236
 
237
        if ($status === 0) {
238
            $this->info('Sail build successful.');
239
        }
240
    }
241
 
242
    /**
243
     * Run the given commands.
244
     *
245
     * @param  array  $commands
246
     * @return int
247
     */
248
    protected function runCommands($commands)
249
    {
250
        $process = Process::fromShellCommandline(implode(' && ', $commands), null, null, null, null);
251
 
252
        if ('\\' !== DIRECTORY_SEPARATOR && file_exists('/dev/tty') && is_readable('/dev/tty')) {
253
            try {
254
                $process->setTty(true);
255
            } catch (RuntimeException $e) {
256
                $this->output->writeln('  <bg=yellow;fg=black> WARN </> '.$e->getMessage().PHP_EOL);
257
            }
258
        }
259
 
260
        return $process->run(function ($type, $line) {
261
            $this->output->write('    '.$line);
262
        });
263
    }
264
}