1
|
|
|
<?php |
2
|
|
|
declare(strict_types=1); |
3
|
|
|
/** |
4
|
|
|
* RoadRunner. |
5
|
|
|
* |
6
|
|
|
* @license MIT |
7
|
|
|
* @author Anton Titov (Wolfy-J) |
8
|
|
|
*/ |
9
|
|
|
|
10
|
|
|
namespace Spiral\RoadRunner\QuickBuild; |
11
|
|
|
|
12
|
|
|
final class Builder |
13
|
|
|
{ |
14
|
|
|
const DOCKER = 'spiralscout/rr-build'; |
15
|
|
|
|
16
|
|
|
/** |
17
|
|
|
* Coloring. |
18
|
|
|
* |
19
|
|
|
* @var array |
20
|
|
|
*/ |
21
|
|
|
protected static $colors = [ |
22
|
|
|
"reset" => "\e[0m", |
23
|
|
|
"white" => "\033[1;38m", |
24
|
|
|
"red" => "\033[0;31m", |
25
|
|
|
"green" => "\033[0;32m", |
26
|
|
|
"yellow" => "\033[1;93m", |
27
|
|
|
"gray" => "\033[0;90m" |
28
|
|
|
]; |
29
|
|
|
|
30
|
|
|
/** @var array */ |
31
|
|
|
private $config; |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* @param array $config |
35
|
|
|
*/ |
36
|
|
|
protected function __construct(array $config) |
37
|
|
|
{ |
38
|
|
|
$this->config = $config; |
39
|
|
|
} |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* Validate the build configuration. |
43
|
|
|
* |
44
|
|
|
* @return array |
45
|
|
|
*/ |
46
|
|
|
public function configErrors(): array |
47
|
|
|
{ |
48
|
|
|
$errors = []; |
49
|
|
|
if (!isset($this->config["commands"])) { |
50
|
|
|
$errors[] = "Directive 'commands' missing"; |
51
|
|
|
} |
52
|
|
|
|
53
|
|
|
if (!isset($this->config["packages"])) { |
54
|
|
|
$errors[] = "Directive 'packages' missing"; |
55
|
|
|
} |
56
|
|
|
|
57
|
|
|
if (!isset($this->config["register"])) { |
58
|
|
|
$errors[] = "Directive 'register' missing"; |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
return $errors; |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* Build the application. |
66
|
|
|
* |
67
|
|
|
* @param string $directory |
68
|
|
|
* @param string $template |
69
|
|
|
* @param string $output |
70
|
|
|
* @param string $version |
71
|
|
|
*/ |
72
|
|
|
public function build(string $directory, string $template, string $output, string $version) |
73
|
|
|
{ |
74
|
|
|
$filename = $directory . "/main.go"; |
75
|
|
|
$output = $output . ($this->getOS() == 'windows' ? '.exe' : ''); |
76
|
|
|
|
77
|
|
|
// step 1, generate template |
78
|
|
|
$this->generate($template, $filename); |
79
|
|
|
|
80
|
|
|
$command = sprintf( |
81
|
|
|
'docker run --rm -v "%s":/mnt -e RR_VERSION=%s -e GOARCH=amd64 -e GOOS=%s %s /bin/bash -c "mv /mnt/main.go main.go; bash compile.sh; cp rr /mnt/%s;"', |
82
|
|
|
$directory, |
83
|
|
|
$version, |
84
|
|
|
$this->getOS(), |
85
|
|
|
self::DOCKER, |
86
|
|
|
$output |
87
|
|
|
); |
88
|
|
|
|
89
|
|
|
self::cprintf("<yellow>%s</reset>\n", $command); |
90
|
|
|
|
91
|
|
|
// run the build |
92
|
|
|
$this->run($command, true); |
93
|
|
|
|
94
|
|
|
if (!file_exists($directory . '/' . $output)) { |
95
|
|
|
self::cprintf("<red>Build has failed!</reset>"); |
96
|
|
|
return; |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
self::cprintf("<green>Build complete!</reset>\n"); |
100
|
|
|
$this->run($directory . '/' . $output, false); |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
/** |
104
|
|
|
* @param string $command |
105
|
|
|
* @param bool $shadow |
106
|
|
|
*/ |
107
|
|
|
protected function run(string $command, bool $shadow = false) |
108
|
|
|
{ |
109
|
|
|
$shadow && self::cprintf("<gray>"); |
110
|
|
|
passthru($command); |
111
|
|
|
$shadow && self::cprintf("</reset>"); |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
/** |
115
|
|
|
* @param string $template |
116
|
|
|
* @param string $filename |
117
|
|
|
*/ |
118
|
|
|
protected function generate(string $template, string $filename) |
119
|
|
|
{ |
120
|
|
|
$body = file_get_contents($template); |
121
|
|
|
|
122
|
|
|
$replace = [ |
123
|
|
|
'// -packages- //' => '"' . join("\"\n\"", $this->config['packages']) . '"', |
124
|
|
|
'// -commands- //' => '_ "' . join("\"\n_ \"", $this->config['commands']) . '"', |
125
|
|
|
'// -register- //' => join("\n", $this->config['register']) |
126
|
|
|
]; |
127
|
|
|
|
128
|
|
|
// compile the template |
129
|
|
|
$result = str_replace(array_keys($replace), array_values($replace), $body); |
130
|
|
|
file_put_contents($filename, $result); |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
/** |
134
|
|
|
* @return string |
135
|
|
|
*/ |
136
|
|
|
protected function getOS(): string |
137
|
|
|
{ |
138
|
|
|
$os = strtolower(PHP_OS); |
139
|
|
|
|
140
|
|
|
if (strpos($os, 'win') !== false) { |
141
|
|
|
return 'windows'; |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
if (strpos($os, 'darwin') !== false) { |
145
|
|
|
return 'darwin'; |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
return "linux"; |
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
/** |
152
|
|
|
* Create new builder using given config. |
153
|
|
|
* |
154
|
|
|
* @param string $config |
155
|
|
|
* @return Builder|null |
156
|
|
|
*/ |
157
|
|
|
public static function loadConfig(string $config): ?Builder |
158
|
|
|
{ |
159
|
|
|
if (!file_exists($config)) { |
160
|
|
|
return null; |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
$configData = json_decode(file_get_contents($config), true); |
164
|
|
|
if (!is_array($configData)) { |
165
|
|
|
return null; |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
return new Builder($configData); |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
/** |
172
|
|
|
* Make colored output. |
173
|
|
|
* |
174
|
|
|
* @param string $format |
175
|
|
|
* @param mixed ...$args |
176
|
|
|
*/ |
177
|
|
|
public static function cprintf(string $format, ...$args) |
178
|
|
|
{ |
179
|
|
|
if (self::isColorsSupported()) { |
180
|
|
|
$format = preg_replace_callback("/<\/?([^>]+)>/", function ($value) { |
181
|
|
|
return self::$colors[$value[1]]; |
182
|
|
|
}, $format); |
183
|
|
|
} else { |
184
|
|
|
$format = preg_replace("/<[^>]+>/", "", $format); |
185
|
|
|
} |
186
|
|
|
|
187
|
|
|
echo sprintf($format, ...$args); |
188
|
|
|
} |
189
|
|
|
|
190
|
|
|
/** |
191
|
|
|
* @return bool |
192
|
|
|
*/ |
193
|
|
|
public static function isWindows(): bool |
194
|
|
|
{ |
195
|
|
|
return \DIRECTORY_SEPARATOR === '\\'; |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
/** |
199
|
|
|
* Returns true if the STDOUT supports colorization. |
200
|
|
|
* |
201
|
|
|
* @codeCoverageIgnore |
202
|
|
|
* @link https://github.com/symfony/Console/blob/master/Output/StreamOutput.php#L94 |
203
|
|
|
* @param mixed $stream |
204
|
|
|
* @return bool |
205
|
|
|
*/ |
206
|
|
|
public static function isColorsSupported($stream = STDOUT): bool |
207
|
|
|
{ |
208
|
|
|
if ('Hyper' === getenv('TERM_PROGRAM')) { |
209
|
|
|
return true; |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
try { |
213
|
|
|
if (\DIRECTORY_SEPARATOR === '\\') { |
214
|
|
|
return ( |
215
|
|
|
function_exists('sapi_windows_vt100_support') |
216
|
|
|
&& @sapi_windows_vt100_support($stream) |
217
|
|
|
) || getenv('ANSICON') !== false |
218
|
|
|
|| getenv('ConEmuANSI') == 'ON' |
219
|
|
|
|| getenv('TERM') == 'xterm'; |
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
if (\function_exists('stream_isatty')) { |
223
|
|
|
return (bool)@stream_isatty($stream); |
224
|
|
|
} |
225
|
|
|
|
226
|
|
|
if (\function_exists('posix_isatty')) { |
227
|
|
|
return (bool)@posix_isatty($stream); |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
$stat = @fstat($stream); |
231
|
|
|
// Check if formatted mode is S_IFCHR |
232
|
|
|
return $stat ? 0020000 === ($stat['mode'] & 0170000) : false; |
233
|
|
|
} catch (\Throwable $e) { |
234
|
|
|
return false; |
235
|
|
|
} |
236
|
|
|
} |
237
|
|
|
} |