ReleasesManager::getValidReleases()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 7
ccs 4
cts 4
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 0
crap 1
1
<?php
2
3
/*
4
 * This file is part of Rocketeer
5
 *
6
 * (c) Maxime Fabre <[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
13
namespace Rocketeer\Services\Releases;
14
15
use Illuminate\Support\Arr;
16
use Rocketeer\Services\Container\Container;
17
use Rocketeer\Traits\ContainerAwareTrait;
18
19
/**
20
 * Provides informations and actions around releases.
21
 */
22
class ReleasesManager
23
{
24
    use ContainerAwareTrait;
25
26
    /**
27
     * Cache of the validation file.
28
     *
29
     * @var array
30
     */
31
    protected $state = [];
32
33
    /**
34
     * Cache of the releases.
35
     *
36
     * @var array
37
     */
38
    public $releases = [];
39
40
    /**
41
     * The next release to come.
42
     *
43
     * @var string
44
     */
45
    protected $nextRelease;
46
47
    /**
48
     * Build a new ReleasesManager.
49
     *
50
     * @param Container $container
51
     */
52 118
    public function __construct(Container $container)
53
    {
54 118
        $this->container = $container;
55 118
        $this->state = $this->getValidationFile();
56 118
    }
57
58
    ////////////////////////////////////////////////////////////////////
59
    /////////////////////////////// RELEASES ///////////////////////////
60
    ////////////////////////////////////////////////////////////////////
61
62
    /**
63
     * Get all the releases on the server.
64
     *
65
     * @return int[]
66
     */
67 118
    public function getReleases()
68
    {
69
        // Get releases on server
70 118
        $connection = $this->connections->getCurrentConnectionKey()->toHandle();
71 118
        if (!array_key_exists($connection, $this->releases)) {
72 118
            $releases = $this->paths->getReleasesFolder();
0 ignored issues
show
Documentation Bug introduced by Maxime Fabre
The method getReleasesFolder does not exist on object<Rocketeer\Services\Environment\Pathfinder>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
73 118
            $releases = (array) $this->bash->listContents($releases);
74
75
            // Filter and sort releases
76 118
            $releases = array_filter($releases, function ($release) {
77 100
                return $this->isRelease($release);
78 118
            });
79
80 118
            rsort($releases);
81
82 118
            $this->releases[$connection] = (array) $releases;
83 118
        }
84
85 118
        return $this->releases[$connection];
86
    }
87
88
    /**
89
     * @param string|int $release
90
     */
91 17
    public function addRelease($release)
92
    {
93 17
        $connection = $this->connections->getCurrentConnectionKey()->name;
94
95 17
        $this->releases[$connection][] = $release;
96 17
        krsort($this->releases[$connection]);
97 17
    }
98
99
    /**
100
     * Get an array of non-current releases.
101
     *
102
     * @return int[]
103
     */
104 2
    public function getNonCurrentReleases()
105
    {
106 2
        return $this->getDeprecatedReleases(1);
107
    }
108
109
    /**
110
     * Get an array of deprecated releases.
111
     *
112
     * @param int|null $treshold
113
     *
114
     * @return int[]
115
     */
116 5
    public function getDeprecatedReleases($treshold = null)
117
    {
118 5
        $releases = $this->getReleases();
119 5
        $treshold = $treshold ?: $this->config->get('remote.keep_releases');
120
121
        // Get first X valid releases
122 5
        $keep = $this->getValidReleases();
123 5
        $keep = array_slice($keep, 0, $treshold);
124
125
        // Compute diff
126 5
        $deprecated = array_diff($releases, $keep);
127 5
        $deprecated = array_values($deprecated);
128
129 5
        return $deprecated;
130
    }
131
132
    /**
133
     * Get an array of valid releases.
134
     *
135
     * @return int[]
0 ignored issues
show
Documentation introduced by Maxime Fabre
Should the return type not be array<integer|string>?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
136
     */
137 6
    public function getValidReleases()
138
    {
139 6
        $valid = array_filter($this->state);
140 6
        $valid = array_keys($valid);
141
142 6
        return $valid;
143
    }
144
145
    /**
146
     * Get an array of invalid releases.
147
     *
148
     * @return int[]
149
     */
150 1
    public function getInvalidReleases()
151
    {
152 1
        $releases = $this->getReleases();
153 1
        $invalid = array_diff($this->state, array_filter($this->state));
154 1
        $invalid = array_keys($invalid);
155
156 1
        return array_intersect($releases, $invalid);
157
    }
158
159
    ////////////////////////////////////////////////////////////////////
160
    ////////////////////////////// PATHS ///////////////////////////////
161
    ////////////////////////////////////////////////////////////////////
162
163
    /**
164
     * Get the path to a release.
165
     *
166
     * @param string $release
167
     *
168 118
     * @return string
169
     */
170 118
    public function getPathToRelease($release)
171
    {
172
        return $this->paths->getReleasesFolder($release);
0 ignored issues
show
Documentation Bug introduced by Maxime Fabre
The method getReleasesFolder does not exist on object<Rocketeer\Services\Environment\Pathfinder>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
173
    }
174
175
    /**
176
     * Get the path to the current release.
177
     *
178
     * @param string|null $folder A folder in the release
179
     *
180 78
     * @return string
181
     */
182 78
    public function getCurrentReleasePath($folder = null)
183
    {
184
        if ($folder) {
185
            $folder = '/'.$folder;
186
        }
187
188
        return $this->getPathToRelease($this->getCurrentRelease().$folder);
189
    }
190
191
    ////////////////////////////////////////////////////////////////////
192 77
    ///////////////////////////// VALIDATION ///////////////////////////
193
    ////////////////////////////////////////////////////////////////////
194 77
195 33
    /**
196 33
     * Get the validation file.
197
     *
198 77
     * @return array
199 19
     */
200
    public function getValidationFile()
201
    {
202
        $file = (array) $this->remoteStorage->get();
203
204
        // Fill the missing releases
205
        $releases = $this->getReleases();
206
        $releases = array_fill_keys($releases, false);
207
208
        // Sort entries
209
        ksort($file);
210 118
        ksort($releases);
211
212 118
        // Replace and resort
213
        $releases = array_replace($releases, $file);
214
        krsort($releases);
215 118
216 118
        return $releases;
217
    }
218
219 118
    /**
220 118
     * Assign a state to a release.
221
     *
222
     * @param string|null $release
223 118
     * @param bool        $state
224 118
     */
225
    public function markRelease($release = null, $state = true)
226 118
    {
227
        $release = $release ?: $this->getCurrentRelease();
228
229
        // If the release is not null, mark it as valid
230
        if ($release) {
231
            $this->state[$release] = $state;
232
            $this->remoteStorage->set($release, $state);
233
            krsort($this->state);
234
        }
235 19
    }
236
237 19
    /**
238
     * Mark a release as valid.
239
     *
240 19
     * @param string|null $release
241 19
     */
242 19
    public function markReleaseAsValid($release = null)
243 19
    {
244 19
        $this->markRelease($release, true);
245 19
    }
246
247
    /**
248
     * Get the state of a release.
249
     *
250
     * @param int $release
251
     *
252 18
     * @return bool
253
     */
254 18
    public function checkReleaseState($release)
255 18
    {
256
        return Arr::get($this->state, $release, false);
257
    }
258
259
    ////////////////////////////////////////////////////////////////////
260
    /////////////////////////// CURRENT RELEASE ////////////////////////
261
    ////////////////////////////////////////////////////////////////////
262
263
    /**
264 28
     * Get the current release.
265
     *
266 28
     * @return string|int|null
267
     */
268
    public function getCurrentRelease()
269
    {
270
        $current = Arr::get($this->getValidReleases(), 0);
271
        $current = $this->sanitizeRelease($current);
272
273
        return $this->nextRelease ?: $current;
274
    }
275
276
    /**
277
     * Get the release before the current one.
278 84
     *
279
     * @param string|null $release A release name
280 84
     *
281 84
     * @return string
282
     */
283 84
    public function getPreviousRelease($release = null)
284
    {
285
        // Get all releases and the current one
286
        $releases = $this->getReleases();
287
        $current = $release ?: $this->getCurrentRelease();
288
289
        // Get the one before that, or default to current
290
        $key = array_search($current, $releases, true);
291
        $key = !is_int($key) ? -1 : $key;
292
        $next = 1;
293 11
        do {
294
            $release = Arr::get($releases, $key + $next);
295
            ++$next;
296 11
        } while (!$this->checkReleaseState($release) && isset($this->state[$release]));
297 11
298
        return $release ?: $current;
299
    }
300 11
301 11
    /**
302 11
     * Get the next release to come.
303
     *
304 11
     * @return string
305 11
     */
306 11
    public function getNextRelease()
307
    {
308 11
        if (!$this->nextRelease) {
309
            $manual = $this->getOption('release');
310
            $manual = $this->isRelease($manual) ? $manual : null;
311
312
            $this->nextRelease = $manual ?: $this->bash->getTimestamp();
0 ignored issues
show
Documentation Bug introduced by Maxime Fabre
The method getTimestamp does not exist on object<Rocketeer\Services\Connections\Shell\Bash>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
313
        }
314
315
        return $this->nextRelease;
316 19
    }
317
318 19
    /**
319 19
     * Change the release to come.
320 19
     *
321
     * @param string $release
322 19
     */
323 19
    public function setNextRelease($release)
324
    {
325 19
        $this->nextRelease = $release;
326
    }
327
328
    //////////////////////////////////////////////////////////////////////
329
    ////////////////////////////// HELPERS ///////////////////////////////
330
    //////////////////////////////////////////////////////////////////////
331
332
    /**
333 4
     * Sanitize a possible release.
334
     *
335 4
     * @param string|int $release
336 4
     *
337
     * @return string|null
338
     */
339
    protected function sanitizeRelease($release)
340
    {
341
        return $this->isRelease($release) ? (string) $release : null;
342
    }
343
344
    /**
345
     * Check if it quacks like a duck.
346
     *
347
     * @param string|int $release
348
     *
349 84
     * @return bool
350
     */
351 84
    protected function isRelease($release)
352
    {
353
        $release = (string) $release;
354
355
        return (bool) preg_match('#[0-9]{14}#', $release);
356
    }
357
}
358