GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( c71d65...ddfe0f )
by Sam
02:35
created

Module::createFpmConfig()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 4

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 22
ccs 12
cts 12
cp 1
rs 9.568
cc 4
nc 8
nop 0
crap 4
1
<?php
2
declare(strict_types=1);
3
4
namespace SamIT\Yii2\PhpFpm;
5
6
use Docker\Context\Context;
7
use Docker\Context\ContextBuilder;
8
use yii\base\InvalidConfigException;
9
use yii\base\UnknownPropertyException;
10
use yii\helpers\ArrayHelper;
11
use yii\helpers\Console;
12
use yii\mutex\Mutex;
13
14
/**
15
 * Class Module
16
 * @package SamIT\Yii2\PhpFpm
17
 * @property-write string[] $additionalPackages
18
 * @property-write string[] $additionalExtensions
19
 * @property-write string|int[] $additionalPoolConfig
20
 * @property-write string[] $additionalPhpConfig
21
 * @property-write string[] $additionalFpmConfig
22
 */
23
class Module extends \yii\base\Module
24
{
25
26
    /**
27
     * @var bool Whether the container should attempt to run migrations on launch.
28
     */
29
    public $runMigrations = false;
30
31
    /**
32
     * @var bool whether migrations should acquire a lock.
33
     * It must be configured in the 'mutex' component of this module or the application
34
     * Note that this mutex must be shared between all instances of your application.
35
     * Consider using something like redis or mysql mutex.
36
     */
37
    public $migrationsUseMutex = true;
38
39
    /**
40
     * The variables will be written to /runtime/env.json as JSON, where your application can read them.
41
     * @var string[] List of required environment variables. If one is missing the container will exit.
42
     *
43
     */
44
    public $environmentVariables = [];
45
46
    /**
47
     * @var array Pool directives
48
     * @see http://php.net/manual/en/install.fpm.configuration.php
49
     *
50
     */
51
    public $poolConfig = [
52
        'user' => 'nobody',
53
        'group' => 'nobody',
54
        'listen' => 9000,
55
        'pm' => 'dynamic',
56
        'pm.max_children' => 40,
57
        'pm.start_servers' => 3,
58
        'pm.min_spare_servers' => 1,
59
        'pm.max_spare_servers' => 3,
60
        'access.log' => '/proc/self/fd/2',
61
        'clear_env' => 'yes',
62
        'catch_workers_output' => 'yes'
63
    ];
64
65
    /**
66
     * @var array PHP configuration, supplied via php_admin_value in fpm config.
67
     */
68
    public $phpConfig = [
69
        'upload_max_filesize' => '20M',
70
        'post_max_size' => '25M'
71
    ];
72
73
    /**
74
     * @var array Global directives
75
     * @see http://php.net/manual/en/install.fpm.configuration.php
76
     *
77
     */
78
    public $fpmConfig = [
79
        'error_log' => '/proc/self/fd/2',
80
        'daemonize' => 'no',
81
    ];
82
83
    /**
84
     * List of OS packages to install
85
     */
86
    public $packages = [
87
        'php7',
88
        'php7-fpm',
89
        'tini',
90
        'ca-certificates',
91
        /**
92
         * @see https://stedolan.github.io/jq/
93
         * This is used for converting the env to JSON.
94
         */
95
        'jq'
96
    ];
97
98
    /**
99
     * List of php extensions to install
100
     */
101
    public $extensions = [
102
        'ctype',
103
        'gd',
104
        'iconv',
105
        'intl',
106
        'json',
107
        'mbstring',
108
        'session',
109
        'pdo_mysql',
110
        'session',
111
        'curl'
112
    ];
113
114
    /**
115
     * @var string The name of the created image.
116
     */
117
    public $image;
118
119
    /**
120
     * @var string The tag of the created image.
121
     */
122
    public $tag = 'latest';
123
124
    /**
125
     * @var bool wheter to push successful builds.
126
     */
127
    public $push = false;
128
129
    /**
130
     * @var string Location of composer.json / composer.lock
131
     */
132
    public $composerFilePath = '@app/../';
133
134
    /**
135
     * @var string[] List of console commands that are executed upon container launch.
136
     */
137
    public $initializationCommands = [];
138
    /**
139
     * @return string A PHP-FPM config file.
140
     */
141 3
    protected function createFpmConfig()
142
    {
143 3
        $config = [];
144
        // Add global directives.
145 3
        $config[] = '[global]';
146 3
        foreach ($this->fpmConfig as $key => $value) {
147 3
            $config[] = "$key = $value";
148
        }
149
150
        // Add pool directives.
151 3
        $poolConfig = $this->poolConfig;
152 3
        foreach($this->phpConfig as $key => $value) {
153 3
            $poolConfig["php_admin_value[$key]"] = $value;
154
        }
155
156 3
        $config[] = '[www]';
157 3
        foreach ($poolConfig as $key => $value) {
158 3
            $config[] = "$key = $value";
159
        }
160
161 3
        return \implode("\n", $config);
162
    }
163
164
    /**
165
     * @return string A shell script that checks for existence of (non-empty) variables and runs php-fpm.
166
     */
167 3
    protected function createEntrypoint(): string
168
    {
169
        // Get the route.
170 3
        $route = "{$this->getUniqueId()}/migrate/up";
171 3
        $script = "/project/{$this->getConsoleEntryScript()}";
172 3
        $result = [];
173 3
        $result[] = '#!/bin/sh';
174
        // Check for variables.
175 3
        foreach($this->environmentVariables as $name) {
176
            $result[] = \strtr('if [ -z "${name}" ]; then echo "Variable \${name} is required."; exit 1; fi', [
177
                '{name}' => $name
178
            ]);
179
        }
180
181
        // Check if runtime directory is writable.
182 3
        $result[] = <<<SH
183
    su nobody -s /bin/touch /runtime/testfile && rm /runtime/testfile;
184
    if [ $? -ne 0 ]; then
185
      echo Runtime directory is not writable;
186
      exit 1
187
    fi
188
SH;
189
190
191
192
        // Check if runtime is a tmpfs.
193 3
        $message = Console::ansiFormat('/runtime should really be a tmpfs.', [Console::FG_RED]);
194 3
        $result[] = <<<SH
195
mount | grep '/runtime type tmpfs';
196
if [ $? -ne 0 ]; then
197 3
  echo $message; 
198
fi
199
SH;
200 3
        $result[] = 'jq -n env > /runtime/env.json';
201
202 3
        if ($this->runMigrations) {
203
            $result[] = <<<SH
204
ATTEMPTS=0
205
while [ \$ATTEMPTS -lt 10 ]; do
206
  # First run migrations.
207
  $script $route --interactive=0
208
  if [ $? -eq 0 ]; then
209
    echo "Migrations done";
210
    break;
211
  fi 
212
  echo "Failed to run migrations, retrying in 10s.";
213
  sleep 10;
214
  let ATTEMPTS=ATTEMPTS+1
215
done
216
217
if [ \$ATTEMPTS -gt 9 ]; then
218
  echo "Migrations failed.."
219
  exit 1;
220
fi
221
SH;
222
        }
223
224 3
        foreach($this->initializationCommands as $route) {
225
            $result[] = "$script $route --interactive=0";
226
        }
227 3
        $result[] = 'exec php-fpm7 --force-stderr --fpm-config /php-fpm.conf';
228 3
        return \implode("\n", $result);
229
    }
230
231 3
    public function createBuildContext(): Context
232
    {
233 3
        $builder = new ContextBuilder();
234
235
        /**
236
         * BEGIN COMPOSER
237
         */
238 3
        $builder->from('composer');
239 3
        $builder->addFile('/build/composer.json', \Yii::getAlias($this->composerFilePath) .'/composer.json');
240 3
        if (\file_exists(\Yii::getAlias($this->composerFilePath) . '/composer.lock')) {
241 3
            $builder->addFile('/build/composer.lock', \Yii::getAlias($this->composerFilePath) . '/composer.lock');
242
        }
243
244 3
        $builder->run('composer global require hirak/prestissimo');
245
246 3
        $builder->run('cd /build && composer install --no-dev --no-autoloader --ignore-platform-reqs --prefer-dist && rm -rf /root/.composer');
247
248
249
        // Add the actual source code.
250 3
        $root = \Yii::getAlias('@app');
251 3
        if (!\is_string($root)) {
252
            throw new \Exception('Alias @app must be defined.');
253
        }
254
255 3
        $builder->addFile('/build/' . \basename($root), $root);
256 3
        $builder->run('cd /build && composer dumpautoload -o');
257
        /**
258
         * END COMPOSER
259
         */
260
261
262 3
        $builder->from('alpine:edge');
263 3
        $packages = $this->packages;
264
265 3
        foreach ($this->extensions as $extension) {
266
            $packages[] = "php7-$extension";
267
        }
268 3
        $builder->run('apk add --update --no-cache ' . \implode(' ', $packages));
269 3
        $builder->run('mkdir /runtime && chown nobody:nobody /runtime');
270 3
        $builder->volume('/runtime');
271 3
        $builder->copy('--from=0 /build', '/project');
272 3
        $builder->add('/entrypoint.sh', $this->createEntrypoint());
273 3
        $builder->run('chmod +x /entrypoint.sh');
274 3
        $builder->add('/php-fpm.conf', $this->createFpmConfig());
275 3
        $builder->run("php-fpm7 --force-stderr --fpm-config /php-fpm.conf -t");
276 3
        $builder->entrypoint('["/sbin/tini", "--", "/entrypoint.sh"]');
277
278
        // Test if we can run a console command.
279 3
        if (\stripos($this->getConsoleEntryScript(), 'codecept') === false) {
280
            $script = "[ -f /project/{$this->getConsoleEntryScript()} ]";
281
            $builder->run($script);
282
        }
283 3
        return $builder->getContext();
284
    }
285
286 3
    public function getLock(int $timeout = 0)
287
    {
288 3
        if ($this->has('mutex')) {
289 1
            $mutex = $this->get('mutex');
290 1
            if ($mutex instanceof Mutex
291 1
                && $mutex->acquire(__CLASS__, $timeout)
292
            ) {
293
                \register_shutdown_function(function() use ($mutex): void {
294
                    $mutex->release(__CLASS__);
295 1
                });
296 1
                return true;
297
            }
298
        }
299 3
        return false;
300
    }
301
302
    /**
303
     * @throws InvalidConfigException in case the app is not configured as expected
304
     * @return string the relative path of the (console) entry script with respect to the project (not app) root.
305
     */
306 3
    public function getConsoleEntryScript(): string
307
    {
308 3
        $full = \array_slice(\debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS), -1)[0]['file'];
309 3
        $relative = \strtr($full, [\dirname(\Yii::getAlias('@app')) => '']);
310 3
        if ($relative === $full){
311
            throw new InvalidConfigException("The console entry script must be located inside the @app directory.");
312
        }
313 3
        return \ltrim($relative, '/');
314
    }
315
316
317 1
    public function __set($name, $value): void
318
    {
319 1
        if (\strncmp($name, 'additional', 10) === 0) {
320 1
            $this->add(\lcfirst(\substr($name, 10)), $value);
321
        } else {
322
            parent::__set($name, $value);
323
        }
324
    }
325
326 1
    private function add($name, array $value): void
327
    {
328 1
        if (!\property_exists($this, $name)) {
329
            throw new UnknownPropertyException("Unknown property $name");
330
        }
331 1
        $this->$name = ArrayHelper::merge($this->$name, $value);
332
    }
333
}
334