Completed
Push — master ( 4e1da5...23755d )
by Andrii
11:56
created

Deployer::findVariants()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
1
<?php
2
3
namespace hidev\components;
4
5
use Dotenv\Dotenv;
0 ignored issues
show
Bug introduced by
The type Dotenv\Dotenv was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
6
use hidev\helpers\Sys;
7
8
class Deployer
9
{
10
    private $dir;
11
    private $path;
12
    private $name;
13
    private $tier;
14
    private $dotenv;
15
16
    public function __construct(string $path = null)
17
    {
18
        if ($path === null) {
19
            $path = $this->findDefaultPath();
20
        } else {
21
            $path = trim($path);
22
        }
23
        if (strncmp($path, '/', 1) !== 0) {
24
            $path = getcwd() . '/' . $path;
25
        }
26
        if (!file_exists($path)) {
27
            throw new \Exception("no env file at $path");
28
        }
29
30
        $this->dir = dirname($path);
31
        $this->path = $path;
32
33
        $this->getComposer()->autoload();
34
35
        $file = basename($path);
36
        $ps = explode('.', $file, 3);
37
        $name = $ps[2];
38
        $ps = explode('-', $name);
39
40
        $this->file = $file;
0 ignored issues
show
Bug Best Practice introduced by
The property file does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
41
        $this->name = $name;
42
        $this->tier = $ps[1] ?? $name;
43
44
        $this->dotenv = $this->createDotenv($this->dir, $this->file);
45
        $this->dotenv->load();
46
47
        $this->checkHost();
48
    }
49
50
    private static $pathVariants = ['beta', 'dev', 'sol-beta', 'sol-dev', 'bladeroot-beta'];
0 ignored issues
show
introduced by
The private property $pathVariants is not used, and could be removed.
Loading history...
51
52
    private function findDefaultPath(): string
53
    {
54
        $dir = basename($this->getCwd());
55
        foreach ($this->findVariants() as $path) {
56
            $name = pathinfo($path, PATHINFO_EXTENSION);
57
            if (strncmp($dir, "${name}-", strlen($name)+1) === 0) {
58
                return $path;
59
            }
60
        }
61
62
        return '.env.dist';
63
    }
64
65
    private function findVariants(): array
66
    {
67
        return glob('.env.*');
0 ignored issues
show
Bug Best Practice introduced by
The expression return glob('.env.*') could return the type false which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
68
    }
69
70
    private function checkHost()
71
    {
72
        $dir = basename($this->dir);
73
        $host = $this->getHost();
74
        if ($dir !== $host) {
75
            die("Dir:$dir and Host:$host doesn't match\n");
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
76
        }
77
    }
78
79
    /**
80
     * Creates Dotenv object.
81
     * Supports both 2 and 3 version of `phpdotenv`
82
     * @param mixed $dir
83
     * @param mixed $file
84
     * @return Dotenv
85
     */
86
    private function createDotenv($dir, $file)
87
    {
88
        if (method_exists(Dotenv::class, 'create')) {
89
            return Dotenv::create($dir, $file);
90
        } else {
91
            return new Dotenv($dir, $file);
92
        }
93
    }
94
95
    public function call(string $name = null)
96
    {
97
        $name = $name ?: 'all';
98
        if (!method_exists($this, $name)) {
99
            throw new \Exception("wrong command: $name");
100
        }
101
102
        $this->{$name}();
103
    }
104
105
    public function all()
106
    {
107
        $this->symlinks();
108
        $this->chmod();
109
        $this->phplog();
110
        $this->up();
111
    }
112
113
    public function symlinks()
114
    {
115
        $envSrc = $this->getPath($this->file);
116
        $envDst = $this->getPath('.env');
117
        $docSrc = $this->findDocker();
118
        $docDst = $this->getPath('docker-compose.yml');
119
120
        Sys::passthru("ln -sf $envSrc $envDst");
121
        Sys::passthru("ln -sf $docSrc $docDst");
122
    }
123
124
    private function findDocker(): string
125
    {
126
        $path = $this->getPath("core/docker-compose.yml.{$this->name}");
127
        if (file_exists($path)) {
128
            return $path;
129
        }
130
        $path = $this->getPath("core/docker-compose.yml.{$this->tier}");
131
        if (file_exists($path)) {
132
            return $path;
133
        }
134
        $tier = $this->getAltTier();
135
        $path = $this->getPath("core/docker-compose.yml.$tier");
136
        if (file_exists($path)) {
137
            return $path;
138
        }
139
140
        throw new \Exception("no docker-compose file: $path");
141
    }
142
143
    private function getAltTier()
144
    {
145
        //return $this->tier === 'beta' ? 'dist' : $this->tier;
146
        return 'dist';
147
    }
148
149
    public function chmod()
150
    {
151
        Sys::chmod('a+w', 'runtime public/assets');
152
    }
153
154
    public function phplog()
155
    {
156
        $phplog = $this->getPath('.docker/php/var/log/php/php.log');
157
        $dir = dirname($phplog);
158
        if (!file_exists($dir)) {
159
            Sys::mkdir($dir);
160
        }
161
        if (!file_exists($phplog)) {
162
            Sys::passthru("sudo touch $phplog");
163
        }
164
        Sys::chmod('a+w', $phplog);
165
    }
166
167
    private $composer;
168
169
    private function getComposer()
170
    {
171
        if ($this->composer === null) {
172
            $this->composer = new Composer($this->dir);
173
        }
174
175
        return $this->composer;
176
    }
177
178
    private function up()
179
    {
180
        $this->refresh();
181
        $this->getSystemd()->up($this->genSystemdConfig());
182
    }
183
184
    private function down()
185
    {
186
        $this->getSystemd()->down();
187
    }
188
189
    private function restart()
190
    {
191
        $this->refresh();
192
        $this->getSystemd()->restart();
193
        $this->getSystemd()->status();
194
    }
195
196
    private function status()
197
    {
198
        $this->refresh();
199
        $this->getSystemd()->status();
200
    }
201
202
    private $systemd;
203
204
    private function getSystemd()
205
    {
206
        if ($this->systemd === null) {
207
            $this->systemd = new Systemd($this->getHost());
208
        }
209
210
        return $this->systemd;
211
    }
212
213
    public function refresh()
214
    {
215
        $this->getComposer()->dump();
216
    }
217
218
    private $systemdTemplate = 'core/systemd.service';
219
220
    private function genSystemdConfig()
221
    {
222
        return $this->genByTemplate($this->systemdTemplate);
223
    }
224
225
    private function genByTemplate($file): string
226
    {
227
        $tpl = file_get_contents($this->findTemplatePath($file));
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->findTemplatePath($file) targeting hidev\components\Deployer::findTemplatePath() 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...
Bug introduced by
$this->findTemplatePath($file) of type void is incompatible with the type string expected by parameter $filename of file_get_contents(). ( Ignorable by Annotation )

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

227
        $tpl = file_get_contents(/** @scrutinizer ignore-type */ $this->findTemplatePath($file));
Loading history...
228
        $subs = [];
229
        foreach ($this->getConfig() as $key => $value) {
230
            $subs["{{$key}}"] = $value;
231
        }
232
233
        return strtr($tpl, $subs);
234
    }
235
236
    /**
237
     * undocumented function
238
     *
239
     * @return void
240
     */
241
    private function findTemplatePath($file)
242
    {
243
        $path = $this->getPath($file);
244
        if (file_exists($path)) {
245
            return $path;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $path returns the type string which is incompatible with the documented return type void.
Loading history...
246
        }
247
        $path = __DIR__ . DIRECTORY_SEPARATOR . $file;
248
        if (file_exists($path)) {
249
            return $path;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $path returns the type string which is incompatible with the documented return type void.
Loading history...
250
        }
251
        $path = __DIR__ . DIRECTORY_SEPARATOR . basename($file);
252
        if (file_exists($path)) {
253
            return $path;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $path returns the type string which is incompatible with the documented return type void.
Loading history...
254
        }
255
256
        throw new \Exception("failed find `$file` template");
257
    }
258
259
    public function getConfig(): array
260
    {
261
        return array_merge(
262
            [
263
                'HOST'  => $this->getHost(),
264
                'DIR'   => $this->dir,
265
            ],
266
            $this->getEnv()
267
        );
268
    }
269
270
    public function getEnv(): array
271
    {
272
        return $_ENV;
273
    }
274
275
    public function getHost(): string
276
    {
277
        $hosts = $this->getHosts();
278
        return $_ENV['HOST'] ?? reset($hosts);
279
    }
280
281
    public function getHosts(): array
282
    {
283
        if (!empty($_ENV['HOSTS'])) {
284
            $hosts = explode(',', $_ENV['HOSTS']);
285
        } elseif (!empty($_ENV['HOST'])) {
286
            $hosts = [$_ENV['HOSTS']];
287
        } else {
288
            throw new \Exception('no HOST given');
289
        }
290
        foreach ($hosts as &$host) {
291
            $host = trim($host);
292
        }
293
294
        return $hosts;
295
    }
296
297
    public function getPath(string $file): string
298
    {
299
        if (strncmp($file, '/', 1) === 0) {
300
            $path = $file;
301
        } else {
302
            $path = $this->dir . DIRECTORY_SEPARATOR . $file;
303
        }
304
305
        return $this->extractCwd($path);
306
    }
307
308
    private function extractCwd(string $path): string
309
    {
310
        $cwd = $this->getCwd();
311
        $len = strlen($cwd);
312
        if (strncmp($path, $cwd, $len) === 0) {
313
            $path = substr($path, $len+1);
314
        }
315
316
        return $path;
317
    }
318
319
    private $cwd;
320
321
    private function getCwd(): string
322
    {
323
        if ($this->cwd === null) {
324
            $this->cwd = \getcwd();
325
        }
326
327
        return $this->cwd;
328
    }
329
}
330