Passed
Pull Request — master (#6)
by ANTHONIUS
02:39
created

Restore::doProcessHooks()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 14
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 14
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 10
nc 1
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the dotfiles project.
7
 *
8
 *     (c) Anthonius Munthi <[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 Dotfiles\Core\Processor;
15
16
use Dotfiles\Core\Config\Config;
17
use Dotfiles\Core\Event\Dispatcher;
18
use Dotfiles\Core\Event\PatchEvent;
19
use Dotfiles\Core\Util\Filesystem;
20
use Dotfiles\Core\Util\Toolkit;
21
use Psr\Log\LoggerInterface;
22
use Symfony\Component\Console\Helper\DebugFormatterHelper;
23
use Symfony\Component\Console\Output\OutputInterface;
24
use Symfony\Component\Finder\Finder;
25
use Symfony\Component\Finder\SplFileInfo;
26
use Symfony\Component\Process\Process;
27
28
class Restore
29
{
30
    /**
31
     * @var Config
32
     */
33
    private $config;
34
35
    /**
36
     * @var Dispatcher
37
     */
38
    private $dispatcher;
39
40
    /**
41
     * @var array
42
     */
43
    private $hooks;
44
45
    /**
46
     * @var LoggerInterface
47
     */
48
    private $logger;
49
50
    /**
51
     * @var OutputInterface
52
     */
53
    private $output;
54
55
    /**
56
     * @var array
57
     */
58
    private $patches = array();
59
60
    /**
61
     * @var string
62
     */
63
    private $section;
64
65
    public function __construct(
66
        Config $config,
67
        Dispatcher $dispatcher,
68
        OutputInterface $output,
69
        LoggerInterface $logger
70
    ) {
71
        $this->config = $config;
72
        $this->output = $output;
73
        $this->logger = $logger;
74
        $this->dispatcher = $dispatcher;
75
    }
76
77
    public function run(): void
78
    {
79
        $output = $this->output;
80
        $machine = getenv('DOTFILES_MACHINE_NAME');
81
        $config = $this->config;
82
        $sections = array('defaults', $machine);
83
84
        $this->registerHooks();
85
        $this->doPreRestoreHooks();
86
        foreach ($sections as $name) {
87
            $this->section = $name;
88
            $output->writeln("Processing <comment>$name</comment> section.");
89
            $backupDir = $config->get('dotfiles.backup_dir')."/src/{$name}";
90
91
            $this->processHomeDir($backupDir.'/home');
92
            $this->registerPatch($backupDir.'/patch');
93
94
            $output->writeln('');
95
            $output->writeln('');
96
        }
97
98
        $this->section = 'patch';
99
        $this->debug('applying patch');
100
        $this->processPatch();
101
102
        $this->doPostRestoreHooks();
103
    }
104
105
    private function debug($message, $options = array()): void
106
    {
107
        $message = sprintf('[%s] %s', $this->section, $message);
108
        $this->output->writeln($message, $options);
109
    }
110
111
    private function doPostRestoreHooks(): void
112
    {
113
        $hooks = $this->hooks['post']['restore'];
114
        $this->output->writeln('Processing post-restore hooks');
115
        foreach ($hooks as $relPath => $realPath) {
116
            $this->doProcessHooks($relPath, $realPath);
117
        }
118
    }
119
120
    private function doPreRestoreHooks(): void
121
    {
122
        $hooks = $this->hooks['pre']['restore'];
123
        $this->output->writeln('Processing pre-restore hooks');
124
        foreach ($hooks as $relPath => $realPath) {
125
            $this->doProcessHooks($relPath, $realPath);
126
        }
127
    }
128
129
    private function doProcessHooks($relPath, $realPath): void
130
    {
131
        $helper = new DebugFormatterHelper();
132
        $output = $this->output;
133
        $output->writeln("Executing <comment>$relPath</comment>");
134
        $process = new Process($realPath);
135
        $process->run(function ($type, $buffer) use ($relPath,$output,$helper,$process): void {
0 ignored issues
show
Unused Code introduced by
The import $relPath is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
136
            $contents = $helper->start(
137
                spl_object_hash($process),
138
                $buffer,
139
                Process::ERR === $type
140
            );
141
142
            $output->writeln(sprintf($contents));
143
        });
144
    }
145
146
    private function processHomeDir($homeDir): void
147
    {
148
        $targetDir = $this->config->get('dotfiles.home_dir');
149
        if (!is_dir($homeDir)) {
150
            $this->debug("Home directory <comment>$homeDir</comment> not found");
151
152
            return;
153
        }
154
155
        $files = Finder::create()
156
            ->in($homeDir)
157
            ->ignoreDotFiles(false)
158
            ->ignoreVCS(true)
159
            ->files()
160
        ;
161
162
        $fs = new Filesystem();
163
        /* @var \Symfony\Component\Finder\SplFileInfo $file */
164
        foreach ($files as $file) {
165
            $target = Toolkit::ensureDotPath($file->getRelativePathname());
166
            $fs->copy($file->getRealPath(), $targetDir.DIRECTORY_SEPARATOR.$target);
167
            $this->debug('+restore: <comment>'.$target.'</comment>');
168
        }
169
    }
170
171
    /**
172
     * Processing all registered patch.
173
     */
174
    private function processPatch(): void
175
    {
176
        $event = new PatchEvent($this->patches);
177
        $dispatcher = $this->dispatcher;
178
        $dispatcher->dispatch(PatchEvent::NAME, $event);
179
180
        $patches = $event->getPatches();
181
        $homeDir = $this->config->get('dotfiles.home_dir');
182
        $fs = new Filesystem();
183
        foreach ($patches as $relPath => $patch) {
184
            $contents = implode(PHP_EOL, $patch);
185
            $target = $homeDir.DIRECTORY_SEPARATOR.$relPath;
186
            $fs->patch($target, $contents);
187
        }
188
    }
189
190
    private function registerHooks(): void
191
    {
192
        $this->hooks['pre']['restore'] = array();
193
        $this->hooks['post']['restore'] = array();
194
195
        $this->section = 'init';
196
        $backupPath = $this->config->get('dotfiles.backup_dir');
197
        $finder = Finder::create()
198
            ->in($backupPath.'/src')
199
            ->path('hooks')
200
            ->name('pre-*')
201
            ->name('post-*')
202
        ;
203
204
        /* @var SplFileInfo $file */
205
        foreach ($finder->files() as $file) {
206
            $relPath = $file->getRelativePathname();
207
            $realPath = $file->getRealPath();
208
209
            $baseName = basename($file->getRealPath());
210
            if (false !== ($tlength = strpos($baseName, '.'))) {
211
                $baseName = substr($baseName, 0, $tlength);
212
            }
213
            $exp = explode('-', $baseName);
214
215
            if (!is_executable($realPath)) {
216
                $this->debug('-hooks not executable: '.$relPath);
217
218
                continue;
219
            }
220
            $type = $exp[0];
221
            $hookOn = $exp[1];
222
            $this->hooks[$type][$hookOn][$relPath] = $realPath;
223
            $this->debug('+hooks '.$relPath);
224
        }
225
    }
226
227
    private function registerPatch($patchDir): void
228
    {
229
        if (!is_dir($patchDir)) {
230
            $this->debug('no patch directory found, skipping');
231
232
            return;
233
        }
234
235
        $finder = Finder::create()
236
            ->ignoreVCS(true)
237
            ->ignoreDotFiles(false)
238
            ->in($patchDir)
239
        ;
240
241
        /* @var SplFileInfo $file */
242
        foreach ($finder->files() as $file) {
243
            $relPath = Toolkit::ensureDotPath($file->getRelativePathname());
244
            $patch = file_get_contents($file->getRealPath());
245
            if (!isset($this->patches[$relPath])) {
246
                $this->patches[$relPath] = array();
247
            }
248
            $this->patches[$relPath][] = $patch;
249
            $this->debug('+patch '.$relPath);
250
        }
251
    }
252
}
253