Worker   A
last analyzed

Complexity

Total Complexity 32

Size/Duplication

Total Lines 240
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 32
lcom 1
cbo 2
dl 0
loc 240
ccs 93
cts 93
cp 1
rs 9.6
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 18 5
A read() 0 21 4
A getCurrentCodeVersion() 0 4 1
A getNewCodeVersion() 0 4 1
C filter() 0 45 8
A deploy() 0 17 2
A commit() 0 13 3
C processPlaybook() 0 32 8
1
<?php
2
3
namespace DeployRevision;
4
5
class Worker implements WorkerInterface
6
{
7
    /**
8
     * Indicates whether deployment was executed.
9
     *
10
     * @var bool
11
     */
12
    private $deployed = false;
13
    /**
14
     * Environment ID.
15
     *
16
     * @var string
17
     */
18
    protected $environment = '';
19
    /**
20
     * List of collected commands from playbooks.
21
     *
22
     * @var array
23
     */
24
    protected $commands = [];
25
    /**
26
     * Path to file for storing code revision.
27
     *
28
     * @var string
29
     */
30
    protected $versionFile = '';
31
    /**
32
     * Current version of code.
33
     *
34
     * @var int
35
     */
36
    protected $currentCodeVersion = 0;
37
    /**
38
     * New version of code.
39
     *
40
     * @var int
41
     */
42
    protected $newCodeVersion = 0;
43
    /**
44
     * An array where keys are playbook names and values are latest deployed versions.
45
     *
46
     * @var int[]
47
     */
48
    protected $completed = [];
49
    /**
50
     * YAML parser.
51
     *
52
     * @var YamlInterface
53
     */
54
    protected $yaml;
55
    /**
56
     * Messages logger.
57
     *
58
     * @var LoggerInterface
59
     */
60
    protected $logger;
61
62
    /**
63
     * {@inheritdoc}
64
     */
65 20
    public function __construct(YamlInterface $yaml, LoggerInterface $logger, $environment, $versionFile)
66
    {
67 20
        if (!$yaml->isAvailable()) {
68 1
            throw new \RuntimeException(sprintf('YAML parser "%s" is not available', get_class($yaml)));
69
        }
70
71 19
        $this->yaml = $yaml;
72 19
        $this->logger = $logger;
73 19
        $this->environment = $environment;
74 19
        $this->versionFile = "$versionFile-$environment";
75
76 19
        if (file_exists($this->versionFile)) {
77 2
            $info = $this->yaml->parse(file_get_contents($this->versionFile));
78
79 2
            $this->completed = isset($info['completed']) ? $info['completed'] : [];
80 2
            $this->newCodeVersion = $this->currentCodeVersion = isset($info['version']) ? (int) $info['version'] : 0;
81 2
        }
82 19
    }
83
84
    /**
85
     * {@inheritdoc}
86
     */
87 17
    public function read($path)
88
    {
89 17
        if (is_dir($path)) {
90
            // Using just "\FilesystemIterator" we cannot be sure that correct ordering will be gained. On
91
            // TravisCI, for instance, ordering always was correct for every PHP version, but on Scrutinizer
92
            // was the cases when this valuable thing has not been achieved.
93
            // https://scrutinizer-ci.com/g/BR0kEN-/deploy-revision/inspections/8b28f584-b923-4a47-96af-90f5b31f4a32
94 7
            $files = iterator_to_array(new \FilesystemIterator($path, \FilesystemIterator::SKIP_DOTS));
95
96
            // Guarantee alphabetical ordering on every file system.
97 7
            ksort($files);
98
99 7
            foreach ($files as $path => $file) {
100 7
                $this->processPlaybook($path);
101 7
            }
102 17
        } elseif (file_exists($path)) {
103 15
            $this->processPlaybook($path);
104 15
        } else {
105 2
            $this->logger->log(sprintf('Not file "%s" nor directory exists', $path));
106
        }
107 16
    }
108
109
    /**
110
     * {@inheritdoc}
111
     */
112 3
    public function getCurrentCodeVersion()
113
    {
114 3
        return $this->currentCodeVersion;
115
    }
116
117
    /**
118
     * {@inheritdoc}
119
     */
120 2
    public function getNewCodeVersion()
121
    {
122 2
        return $this->newCodeVersion;
123
    }
124
125
    /**
126
     * {@inheritdoc}
127
     */
128 11
    public function filter(callable $processor)
129
    {
130 11
        $commands = [];
131
132 11
        ksort($this->commands);
133
134 11
        foreach ($this->commands as $list) {
135 11
            foreach ($list as $command) {
136
                // Deployment cannot continue.
137 11
                if (!is_string($command)) {
138 1
                    throw new \RuntimeException(sprintf(
139 1
                        'Complex value cannot be a command: %s',
140 1
                        var_export($command, true)
141 1
                    ));
142
                }
143
144 10
                $commands[$command] = $command;
145
146
                unset($commands[$processor($command, $commands, function (
147
                    $return_previous,
148
                    array $current_commands,
149
                    array $existing_commands
150
                ) use (
151 2
                    $command,
152 2
                    $commands
153
                ) {
154
                    // Ensure that current command is candidate for filtering.
155 2
                    if (in_array($command, $current_commands)) {
156
                        // Iterate over commands in the list.
157 2
                        foreach ($commands as $existing_command) {
158
                            // Match the command in diapason.
159 2
                            if (in_array($existing_command, $existing_commands)) {
160
                                // Remove existing command or do not add currently processed.
161 2
                                return $return_previous ? $existing_command : $command;
162
                            }
163 2
                        }
164 2
                    }
165
166 2
                    return '';
167 10
                })]);
168 10
            }
169 10
        }
170
171 10
        $this->commands = array_values($commands);
172 10
    }
173
174
    /**
175
     * {@inheritdoc}
176
     */
177 12
    public function deploy(callable $processor)
178
    {
179 12
        $command = reset($this->commands);
180
181
        // Filtering have not been performed and we dealing with array of arrays.
182 12
        if (is_array($command)) {
183 9
            $this->filter(function () {
184 8
                return '';
185 9
            });
186 8
        }
187
188 11
        array_map($processor, $this->commands);
189
190
        // Do not allow to deploy once again accidentally.
191 11
        $this->commands = [];
192 11
        $this->deployed = true;
193 11
    }
194
195
    /**
196
     * {@inheritdoc}
197
     */
198 3
    public function commit()
199
    {
200 3
        if (!$this->deployed) {
201 1
            throw new \RuntimeException('Deployment has not been performed. Saving the revision will cause problems');
202
        }
203
204 2
        if (!@file_put_contents($this->versionFile, $this->yaml->dump([
205 2
            'version' => $this->newCodeVersion,
206 2
            'completed' => $this->completed,
207 2
        ]))) {
208 1
            throw new \RuntimeException(sprintf('Cannot save the version of code to "%s" file', $this->versionFile));
209
        }
210 1
    }
211
212 15
    protected function processPlaybook($path)
213
    {
214 15
        if (!in_array(pathinfo($path, PATHINFO_EXTENSION), ['yaml', 'yml'])) {
215 1
            return;
216
        }
217
218 15
        $contents = $this->yaml->parse(file_get_contents($path));
219
220 15
        if (empty($contents['commands'])) {
221 1
            return;
222
        }
223
224 15
        foreach ($contents['commands'] as $group => $commands_group) {
225
            // Get only actions for particular site or global ones.
226 15
            if (!in_array($group, ['global', $this->environment])) {
227 2
                continue;
228
            }
229
230 15
            foreach ($commands_group as $version => $commands) {
231
                // Skip code actions that were already run.
232 15
                if ($version <= $this->currentCodeVersion && isset($this->completed[$path])) {
233 1
                    continue;
234
                }
235
236
                // Group commands by version to guarantee exact order.
237 14
                $this->commands += [$version => []];
238 14
                $this->commands[$version] = array_merge($this->commands[$version], array_filter((array) $commands));
239 14
                $this->completed[$path] = $version;
240 14
                $this->newCodeVersion = (int) max($this->newCodeVersion, $version);
241 15
            }
242 15
        }
243 15
    }
244
}
245