1 | <?php |
||
2 | |||
3 | declare(strict_types=1); |
||
4 | |||
5 | /** |
||
6 | * This file is part of Laravel Zero. |
||
7 | * |
||
8 | * (c) Nuno Maduro <[email protected]> |
||
9 | * |
||
10 | * For the full copyright and license information, please view the LICENSE |
||
11 | * file that was distributed with this source code. |
||
12 | */ |
||
13 | |||
14 | namespace LaravelZero\Framework\Commands; |
||
15 | |||
16 | use Illuminate\Support\Facades\File; |
||
17 | use Symfony\Component\Process\Process; |
||
18 | use Illuminate\Console\Application as Artisan; |
||
19 | use Symfony\Component\Console\Output\NullOutput; |
||
20 | use Symfony\Component\Console\Helper\ProgressBar; |
||
21 | use Symfony\Component\Console\Input\InputInterface; |
||
22 | use Symfony\Component\Console\Output\OutputInterface; |
||
23 | |||
24 | final class BuildCommand extends Command |
||
25 | { |
||
26 | /** |
||
27 | * {@inheritdoc} |
||
28 | */ |
||
29 | protected $signature = 'app:build {name? : The build name}'; |
||
30 | |||
31 | /** |
||
32 | * {@inheritdoc} |
||
33 | */ |
||
34 | protected $description = 'Build a single file executable'; |
||
35 | |||
36 | /** |
||
37 | * Holds the configuration on is original state. |
||
38 | * |
||
39 | * @var string|null |
||
40 | */ |
||
41 | private static $config; |
||
42 | |||
43 | /** |
||
44 | * Holds the command original output. |
||
45 | * |
||
46 | * @var \Symfony\Component\Console\Output\OutputInterface |
||
47 | */ |
||
48 | private $originalOutput; |
||
49 | |||
50 | /** |
||
51 | * {@inheritdoc} |
||
52 | */ |
||
53 | 2 | public function handle() |
|
54 | { |
||
55 | 2 | $this->title('Building process'); |
|
56 | |||
57 | 2 | $this->build($this->input->getArgument('name') ?? $this->getBinary()); |
|
58 | 1 | } |
|
59 | |||
60 | /** |
||
61 | * {@inheritdoc} |
||
62 | */ |
||
63 | 2 | public function run(InputInterface $input, OutputInterface $output) |
|
64 | { |
||
65 | 2 | parent::run($input, $this->originalOutput = $output); |
|
66 | 1 | } |
|
67 | |||
68 | /** |
||
69 | * Builds the application into a single file. |
||
70 | */ |
||
71 | 2 | private function build(string $name): BuildCommand |
|
72 | { |
||
73 | /* |
||
74 | * We prepare the application for a build, moving it to production. Then, |
||
75 | * after compile all the code to a single file, we move the built file |
||
76 | * to the builds folder with the correct permissions. |
||
77 | */ |
||
78 | 2 | $this->prepare() |
|
79 | 2 | ->compile($name) |
|
80 | 1 | ->clear(); |
|
81 | |||
82 | 1 | $this->output->writeln( |
|
83 | 1 | sprintf(' Compiled successfully: <fg=green>%s</>', $this->app->buildsPath($name)) |
|
84 | ); |
||
85 | |||
86 | 1 | return $this; |
|
87 | } |
||
88 | |||
89 | 2 | private function compile(string $name): BuildCommand |
|
90 | { |
||
91 | 2 | if (! File::exists($this->app->buildsPath())) { |
|
92 | 2 | File::makeDirectory($this->app->buildsPath()); |
|
93 | } |
||
94 | |||
95 | 2 | $process = new Process( |
|
96 | 2 | './box compile'.' --working-dir='.base_path().' --config='.base_path('box.json'), |
|
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||
97 | 2 | dirname(dirname(__DIR__)).'/bin' |
|
98 | ); |
||
99 | |||
100 | 2 | $section = tap($this->originalOutput->section())->write(''); |
|
101 | |||
102 | 2 | $progressBar = tap( |
|
103 | 2 | new ProgressBar( |
|
104 | 2 | $this->output->getVerbosity() > OutputInterface::VERBOSITY_NORMAL ? new NullOutput() : $section, 25 |
|
105 | ) |
||
106 | 2 | )->setProgressCharacter("\xF0\x9F\x8D\xBA"); |
|
107 | |||
108 | 2 | foreach (tap($process)->start() as $type => $data) { |
|
109 | 2 | $progressBar->advance(); |
|
110 | |||
111 | 2 | if ($this->output->getVerbosity() > OutputInterface::VERBOSITY_NORMAL) { |
|
112 | 2 | $process::OUT === $type ? $this->info("$data") : $this->error("$data"); |
|
113 | } |
||
114 | } |
||
115 | |||
116 | 2 | $progressBar->finish(); |
|
117 | |||
118 | 2 | $section->clear(); |
|
119 | |||
120 | 1 | $this->task(' 2. <fg=yellow>Compile</> into a single file'); |
|
121 | |||
122 | 1 | $this->output->newLine(); |
|
123 | |||
124 | 1 | File::move($this->app->basePath($this->getBinary()).'.phar', $this->app->buildsPath($name)); |
|
125 | |||
126 | 1 | return $this; |
|
127 | } |
||
128 | |||
129 | 2 | private function prepare(): BuildCommand |
|
130 | { |
||
131 | 2 | $file = $this->app->configPath('app.php'); |
|
132 | 2 | static::$config = File::get($file); |
|
133 | 2 | $config = include $file; |
|
134 | |||
135 | 2 | $config['production'] = true; |
|
136 | |||
137 | 2 | $this->task( |
|
138 | 2 | ' 1. Moving application to <fg=yellow>production mode</>', |
|
139 | function () use ($file, $config) { |
||
140 | 2 | File::put($file, '<?php return '.var_export($config, true).';'.PHP_EOL); |
|
141 | 2 | } |
|
142 | ); |
||
143 | |||
144 | 2 | return $this; |
|
145 | } |
||
146 | |||
147 | 2 | private function clear(): BuildCommand |
|
148 | { |
||
149 | 2 | File::put($this->app->configPath('app.php'), static::$config); |
|
150 | |||
151 | 2 | static::$config = null; |
|
152 | |||
153 | 2 | return $this; |
|
154 | } |
||
155 | |||
156 | /** |
||
157 | * Returns the artisan binary. |
||
158 | */ |
||
159 | 2 | private function getBinary(): string |
|
160 | { |
||
161 | 2 | return str_replace("'", '', Artisan::artisanBinary()); |
|
162 | } |
||
163 | |||
164 | /** |
||
165 | * Makes sure that the `clear` is performed even |
||
166 | * if the command fails. |
||
167 | * |
||
168 | * @return void |
||
169 | */ |
||
170 | 30 | public function __destruct() |
|
171 | { |
||
172 | 30 | if (static::$config !== null) { |
|
173 | 1 | $this->clear(); |
|
174 | } |
||
175 | 30 | } |
|
176 | } |
||
177 |