Config::isLoadedFromFile()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
3
/**
4
 * This file is part of CaptainHook
5
 *
6
 * (c) Sebastian Feldmann <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace CaptainHook\App;
13
14
use CaptainHook\App\Config\Run;
15
use InvalidArgumentException;
16
use SebastianFeldmann\Camino\Check;
17
18
/**
19
 * Class Config
20
 *
21
 * @package CaptainHook
22
 * @author  Sebastian Feldmann <[email protected]>
23
 * @link    https://github.com/captainhook-git/captainhook
24
 * @since   Class available since Release 0.9.0
25
 * @internal
26
 */
27
class Config
28
{
29
    /**
30
     * Path to the config file
31
     *
32
     * @var string
33
     */
34
    private string $path;
35
36
    /**
37
     * Does the config file exist
38
     *
39
     * @var bool
40
     */
41
    private bool $fileExists;
42
43
    /**
44
     * CaptainHook settings
45
     *
46
     * @var array<string, string>
47
     */
48
    private array $settings;
49
50
    /**
51
     * All options related to running CaptainHook
52
     *
53
     * @var \CaptainHook\App\Config\Run
54
     */
55
    private Run $runConfig;
56
57
    /**
58
     * List of users custom settings
59
     *
60
     * @var array<string, mixed>
61
     */
62
    private array $custom = [];
63
64
    /**
65
     * List of plugins
66
     *
67
     * @var array<string, \CaptainHook\App\Config\Plugin>
68
     */
69
    private array $plugins = [];
70
71
    /**
72
     * List of hook configs
73
     *
74
     * @var array<string, \CaptainHook\App\Config\Hook>
75
     */
76
    private array $hooks = [];
77
78
    /**
79
     * Config constructor
80
     *
81
     * @param string               $path
82
     * @param bool                 $fileExists
83
     * @param array<string, mixed> $settings
84
     */
85 154
    public function __construct(string $path, bool $fileExists = false, array $settings = [])
86
    {
87 154
        $settings = $this->setupPlugins($settings);
88 154
        $settings = $this->setupCustom($settings);
89 154
        $settings = $this->setupRunConfig($settings);
90
91
92 154
        $this->path       = $path;
93 154
        $this->fileExists = $fileExists;
94 154
        $this->settings   = $settings;
95
96 154
        foreach (Hooks::getValidHooks() as $hook => $value) {
97 154
            $this->hooks[$hook] = new Config\Hook($hook);
98
        }
99
    }
100
101
    /**
102
     * Extract custom settings from Captain Hook ones
103
     *
104
     * @param  array<string, mixed> $settings
105
     * @return array<string, mixed>
106
     */
107 154
    private function setupCustom(array $settings): array
108
    {
109
        /* @var array<string, mixed> $custom */
110 154
        $this->custom = $settings['custom'] ?? [];
111 154
        unset($settings['custom']);
112
113 154
        return $settings;
114
    }
115
116
    /**
117
     * Setup all configured plugins
118
     *
119
     * @param  array<string, mixed> $settings
120
     * @return array<string, mixed>
121
     */
122 154
    private function setupPlugins(array $settings): array
123
    {
124
        /* @var array<int, array<string, mixed>> $pluginSettings */
125 154
        $pluginSettings = $settings['plugins'] ?? [];
126 154
        unset($settings['plugins']);
127
128 154
        foreach ($pluginSettings as $plugin) {
129 1
            $name                 = (string) $plugin['plugin'];
130 1
            $options              = isset($plugin['options']) && is_array($plugin['options'])
131 1
                ? $plugin['options']
132 1
                : [];
133 1
            $this->plugins[$name] = new Config\Plugin($name, $options);
134
        }
135 154
        return $settings;
136
    }
137
138
    /**
139
     * Extract all running related settings into a run configuration
140
     *
141
     * @param  array<string, mixed> $settings
142
     * @return array<string, mixed>
143
     */
144 154
    private function setupRunConfig(array $settings): array
145
    {
146
        // extract the legacy settings
147 154
        $settingsToMove = [
148 154
            Config\Settings::RUN_MODE,
149 154
            Config\Settings::RUN_EXEC,
150 154
            Config\Settings::RUN_PATH,
151 154
            Config\Settings::RUN_GIT
152 154
        ];
153 154
        $config = [];
154 154
        foreach ($settingsToMove as $setting) {
155 154
            if (!empty($settings[$setting])) {
156 10
                $config[substr($setting, 4)] = $settings[$setting];
157
            }
158 154
            unset($settings[$setting]);
159
        }
160
        // make sure the new run configuration supersedes the legacy settings
161 154
        if (isset($settings['run']) && is_array($settings['run'])) {
162 1
            $config = array_merge($config, $settings['run']);
163 1
            unset($settings['run']);
164
        }
165 154
        $this->runConfig = new Run($config);
166 154
        return $settings;
167
    }
168
169
    /**
170
     * Is configuration loaded from file
171
     *
172
     * @return bool
173
     */
174 46
    public function isLoadedFromFile(): bool
175
    {
176 46
        return $this->fileExists;
177
    }
178
179
    /**
180
     * Are actions allowed to fail without stopping the git operation
181
     *
182
     * @return bool
183
     */
184 5
    public function isFailureAllowed(): bool
185
    {
186 5
        return (bool) ($this->settings[Config\Settings::ALLOW_FAILURE] ?? false);
187
    }
188
189
    /**
190
     * @param  string $hook
191
     * @param  bool   $withVirtual if true, also check if hook is enabled through any enabled virtual hook
192
     * @return bool
193
     */
194 16
    public function isHookEnabled(string $hook, bool $withVirtual = true): bool
195
    {
196
        // either this hook is explicitly enabled
197 16
        $hookConfig = $this->getHookConfig($hook);
198 16
        if ($hookConfig->isEnabled()) {
199 10
            return true;
200
        }
201
202
        // or any virtual hook that triggers it is enabled
203 9
        if ($withVirtual && Hooks::triggersVirtualHook($hookConfig->getName())) {
204 7
            $virtualHookConfig = $this->getHookConfig(Hooks::getVirtualHook($hookConfig->getName()));
205 7
            if ($virtualHookConfig->isEnabled()) {
206 4
                return true;
207
            }
208
        }
209
210 9
        return false;
211
    }
212
213
    /**
214
     * Path getter
215
     *
216
     * @return string
217
     */
218 60
    public function getPath(): string
219
    {
220 60
        return $this->path;
221
    }
222
223
    /**
224
     * Return git directory path if configured, CWD/.git if not
225
     *
226
     * @return string
227
     */
228 33
    public function getGitDirectory(): string
229
    {
230 33
        if (empty($this->settings[Config\Settings::GIT_DIR])) {
231 1
            return getcwd() . '/.git';
232
        }
233
234
        // if repo path is absolute use it otherwise create an absolute path relative to the configuration file
235 32
        return Check::isAbsolutePath($this->settings[Config\Settings::GIT_DIR])
236 29
            ? $this->settings[Config\Settings::GIT_DIR]
237 32
            : dirname($this->path) . '/' . $this->settings[Config\Settings::GIT_DIR];
238
    }
239
240
    /**
241
     * Return bootstrap file if configured, CWD/vendor/autoload.php by default
242
     *
243
     * @param  string $default
244
     * @return string
245
     */
246 18
    public function getBootstrap(string $default = 'vendor/autoload.php'): string
247
    {
248 18
        return !empty($this->settings[Config\Settings::BOOTSTRAP])
249 5
            ? $this->settings[Config\Settings::BOOTSTRAP]
250 18
            : $default;
251
    }
252
253
    /**
254
     * Return the configured verbosity
255
     *
256
     * @return string
257
     */
258 34
    public function getVerbosity(): string
259
    {
260 34
        return !empty($this->settings[Config\Settings::VERBOSITY])
261 2
            ? $this->settings[Config\Settings::VERBOSITY]
262 34
            : 'normal';
263
    }
264
265
    /**
266
     * Should the output use ansi colors
267
     *
268
     * @return bool
269
     */
270 3
    public function useAnsiColors(): bool
271
    {
272 3
        return (bool) ($this->settings[Config\Settings::COLORS] ?? true);
273
    }
274
275
    /**
276
     * Get configured php-path
277
     *
278
     * @return string
279
     */
280 58
    public function getPhpPath(): string
281
    {
282 58
        return (string) ($this->settings[Config\Settings::PHP_PATH] ?? '');
283
    }
284
285
    /**
286
     * Get run configuration
287
     *
288
     * @return \CaptainHook\App\Config\Run
289
     */
290 19
    public function getRunConfig(): Run
291
    {
292 19
        return $this->runConfig;
293
    }
294
295
    /**
296
     * Returns the users custom config values
297
     *
298
     * @return array<mixed>
299
     */
300 2
    public function getCustomSettings(): array
301
    {
302 2
        return $this->custom;
303
    }
304
305
    /**
306
     * Whether to abort the hook as soon as a any action has errored. Default is true.
307
     * Otherwise, all actions get executed (even if some of them have failed) and
308
     * finally, a non-zero exit code is returned if any action has errored.
309
     *
310
     * @return bool
311
     */
312 6
    public function failOnFirstError(): bool
313
    {
314 6
        return (bool) ($this->settings[Config\Settings::FAIL_ON_FIRST_ERROR] ?? true);
315
    }
316
317
    /**
318
     * Return config for given hook
319
     *
320
     * @param  string $hook
321
     * @return \CaptainHook\App\Config\Hook
322
     * @throws \InvalidArgumentException
323
     */
324 62
    public function getHookConfig(string $hook): Config\Hook
325
    {
326 62
        if (!Hook\Util::isValid($hook)) {
327 1
            throw new InvalidArgumentException('Invalid hook name: ' . $hook);
328
        }
329 61
        return $this->hooks[$hook];
330
    }
331
332
    /**
333
     * Return hook configs
334
     *
335
     * @return array<string, \CaptainHook\App\Config\Hook>
336
     */
337 6
    public function getHookConfigs(): array
338
    {
339 6
        return $this->hooks;
340
    }
341
342
    /**
343
     * Returns a hook config containing all the actions to execute
344
     *
345
     * Returns all actions from the triggered hook but also any actions of virtual hooks that might be triggered.
346
     * E.g. 'post-rewrite' or 'post-checkout' trigger the virtual/artificial 'post-change' hook.
347
     * Virtual hooks are special hooks to simplify configuration.
348
     *
349
     * @param  string $hook
350
     * @return \CaptainHook\App\Config\Hook
351
     */
352 8
    public function getHookConfigToExecute(string $hook): Config\Hook
353
    {
354 8
        $config     = new Config\Hook($hook, true);
355 8
        $hookConfig = $this->getHookConfig($hook);
356 8
        $config->addAction(...$hookConfig->getActions());
357 8
        if (Hooks::triggersVirtualHook($hookConfig->getName())) {
358 1
            $vHookConfig = $this->getHookConfig(Hooks::getVirtualHook($hookConfig->getName()));
359 1
            if ($vHookConfig->isEnabled()) {
360 1
                $config->addAction(...$vHookConfig->getActions());
361
            }
362
        }
363 8
        return $config;
364
    }
365
366
    /**
367
     * Return plugins
368
     *
369
     * @return Config\Plugin[]
370
     */
371 9
    public function getPlugins(): array
372
    {
373 9
        return $this->plugins;
374
    }
375
376
    /**
377
     * Return config array to write to disc
378
     *
379
     * @return array<string, mixed>
380
     */
381 12
    public function getJsonData(): array
382
    {
383 12
        $data   = [];
384 12
        $config = $this->getConfigJsonData();
385
386 12
        if (!empty($config)) {
387 3
            $data['config'] = $config;
388
        }
389
390 12
        foreach (Hooks::getValidHooks() as $hook => $value) {
391 12
            if ($this->hooks[$hook]->isEnabled() || $this->hooks[$hook]->hasActions()) {
392 8
                $data[$hook] = $this->hooks[$hook]->getJsonData();
393
            }
394
        }
395 12
        return $data;
396
    }
397
398
    /**
399
     * Build the "config" JSON section of the configuration file
400
     *
401
     * @return array<string, mixed>
402
     */
403 12
    private function getConfigJsonData(): array
404
    {
405 12
        $config = !empty($this->settings) ? $this->settings : [];
406
407 12
        $runConfigData = $this->runConfig->getJsonData();
408 12
        if (!empty($runConfigData)) {
409 2
            $config['run'] = $runConfigData;
410
        }
411 12
        if (!empty($this->plugins)) {
412 1
            $config['plugins'] = $this->getPluginsJsonData();
413
        }
414 12
        if (!empty($this->custom)) {
415 1
            $config['custom'] = $this->custom;
416
        }
417 12
        return $config;
418
    }
419
420
    /**
421
     * Collect and return plugin json data for all plugins
422
     *
423
     * @return array<int, mixed>
424
     */
425 1
    private function getPluginsJsonData(): array
426
    {
427 1
        $plugins = [];
428 1
        foreach ($this->plugins as $plugin) {
429 1
            $plugins[] = $plugin->getJsonData();
430
        }
431 1
        return $plugins;
432
    }
433
}
434