Passed
Push — test ( e74b8d...602675 )
by Tom
02:32
created

Env::resetStepRunNumber()   A

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
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
/* this file is part of pipelines */
4
5
namespace Ktomk\Pipelines\Runner;
6
7
use Ktomk\Pipelines\Cli\Args\Args;
8
use Ktomk\Pipelines\Cli\Args\Collector;
9
use Ktomk\Pipelines\Lib;
10
use Ktomk\Pipelines\LibFsPath;
11
12
/**
13
 * Pipeline environment collaborator
14
 */
15
class Env
16
{
17
    /**
18
     * @var array pipelines (bitbucket) environment variables
19
     */
20
    private $vars = array();
21
22
    /**
23
     * collected arguments
24
     *
25
     * @var array
26
     */
27
    private $collected = array();
28
29
    /**
30
     * environment variables to inherit from
31
     *
32
     * @var array
33
     */
34
    private $inherit = array();
35
36
    /**
37
     * @var EnvResolver
38
     */
39
    private $resolver;
40
41
    /**
42
     * create with no default initialization
43
     *
44
     * @param null|array $inherit
45
     *
46
     * @return Env
47
     */
48 3
    public static function create(array $inherit = null)
49
    {
50 3
        $env = new self();
51 3
        $env->initInherit((array)$inherit);
52
53 3
        return $env;
54
    }
55
56
    /**
57
     * @param null|array $inherit
58
     *
59
     * @return Env
60
     */
61 18
    public static function createEx(array $inherit = null)
62
    {
63 18
        $env = new self();
64 18
        $env->initDefaultVars((array)$inherit);
65
66 18
        return $env;
67
    }
68
69
    /**
70
     * @param array $vars
71
     *
72
     * @return array w/ a string variable definition (name=value) per value
73
     */
74 13
    public static function createVarDefinitions(array $vars)
75
    {
76 13
        $array = array();
77
78 13
        foreach ($vars as $name => $value) {
79 11
            if (isset($value)) {
80 11
                $array[] = sprintf('%s=%s', $name, $value);
81
            }
82
        }
83
84 13
        return $array;
85
    }
86
87
    /**
88
     * @param string $option "-e" typically for Docker binary
89
     * @param array $vars variables as hashmap
90
     *
91
     * @return array of options (from $option) and values, ['-e', 'val1', '-e', 'val2', ...]
92
     */
93 13
    public static function createArgVarDefinitions($option, array $vars)
94
    {
95 13
        $args = array();
96
97 13
        foreach (self::createVarDefinitions($vars) as $definition) {
98 11
            $args[] = $option;
99 11
            $args[] = $definition;
100
        }
101
102 13
        return $args;
103
    }
104
105
    /**
106
     * Initialize default environment used in a Bitbucket Pipeline
107
     *
108
     * As the runner mimics some values, defaults are available
109
     *
110
     * @param array $inherit Environment variables to inherit from
111
     *
112
     * @return void
113
     */
114 23
    public function initDefaultVars(array $inherit)
115
    {
116 23
        $this->initInherit($inherit);
117
118
        $inheritable = array(
119 23
            'BITBUCKET_BOOKMARK' => null,
120
            'BITBUCKET_BRANCH' => null,
121 23
            'BITBUCKET_BUILD_NUMBER' => '0',
122 23
            'BITBUCKET_STEP_RUN_NUMBER' => '1',
123 23
            'BITBUCKET_COMMIT' => '0000000000000000000000000000000000000000',
124 23
            'BITBUCKET_REPO_OWNER' => '' . Lib::r($inherit['USER'], 'nobody'),
125 23
            'BITBUCKET_REPO_SLUG' => 'local-has-no-slug',
126
            'BITBUCKET_TAG' => null,
127 23
            'CI' => 'true',
128
            'PIPELINES_CONTAINER_NAME' => null,
129
            'PIPELINES_IDS' => null,
130
            'PIPELINES_PARENT_CONTAINER_NAME' => null,
131
            'PIPELINES_PIP_CONTAINER_NAME' => null,
132
            'PIPELINES_PROJECT_PATH' => null,
133
        );
134
135
        $invariant = array(
136 23
            'PIPELINES_ID' => null,
137
        );
138
139 23
        foreach ($inheritable as $name => $value) {
140 23
            isset($inherit[$name]) ? $inheritable[$name] = $inherit[$name] : null;
141
        }
142
143 23
        $var = $invariant + $inheritable;
144 23
        ksort($var);
145
146 23
        $this->vars = $var;
147 23
    }
148
149
    /**
150
     * Map reference to environment variable setting
151
     *
152
     * Only add the BITBUCKET_BOOKMARK/_BRANCH/_TAG variable
153
     * if not yet set.
154
     *
155
     * @param Reference $ref
156
     *
157
     * @return void
158
     */
159 4
    public function addReference(Reference $ref)
160
    {
161 4
        if (null === $type = $ref->getType()) {
162 2
            return;
163
        }
164
165
        $map = array(
166 4
            'bookmark' => 'BITBUCKET_BOOKMARK',
167
            'branch' => 'BITBUCKET_BRANCH',
168
            'tag' => 'BITBUCKET_TAG',
169
            'pr' => 'BITBUCKET_BRANCH',
170
        );
171
172 4
        if (!isset($map[$type])) {
173 1
            throw new \UnexpectedValueException(sprintf('Unknown reference type: "%s"', $type));
174
        }
175
176 3
        if ($this->addPrReference($ref)) {
177 1
            return;
178
        }
179
180 2
        $this->addVar($map[$type], $ref->getName());
181 2
    }
182
183
    /**
184
     * add a variable if not yet set (real add new)
185
     *
186
     * @param string $name
187
     * @param string $value
188
     *
189
     * @return void
190
     */
191 3
    public function addVar($name, $value)
192
    {
193 3
        if (!isset($this->vars[$name])) {
194 2
            $this->vars[$name] = $value;
195
        }
196 3
    }
197
198
    /**
199
     * @param Reference $reference
200
     *
201
     * @return bool
202
     */
203 3
    public function addPrReference(Reference $reference)
204
    {
205 3
        if ('pr' !== $reference->getType()) {
206 2
            return false;
207
        }
208
209 1
        $var = array_combine(
210 1
            array('BITBUCKET_BRANCH', 'BITBUCKET_PR_DESTINATION_BRANCH'),
211 1
            explode(':', $reference->getName(), 2) + array(null, null)
212
        );
213
214 1
        foreach ($var as $name => $value) {
215 1
            isset($value) && $this->addVar($name, $value);
216
        }
217
218 1
        return true;
219
    }
220
221
    /**
222
     * @param string $name of container
223
     *
224
     * @return void
225
     */
226 2
    public function setContainerName($name)
227
    {
228 2
        if (isset($this->vars['PIPELINES_CONTAINER_NAME'])) {
229 2
            $this->vars['PIPELINES_PARENT_CONTAINER_NAME']
230 2
                = $this->vars['PIPELINES_CONTAINER_NAME'];
231
        }
232
233 2
        $this->vars['PIPELINES_CONTAINER_NAME'] = $name;
234 2
        $this->setFirstPipelineVariable('PIPELINES_PIP_CONTAINER_NAME', $name);
235 2
    }
236
237
    /**
238
     * set the pipelines environment's running pipeline id
239
     *
240
     * @param string $id of pipeline, e.g. "default" or "branch/feature/*"
241
     *
242
     * @return bool whether was used before (endless pipelines in pipelines loop)
243
     */
244 2
    public function setPipelinesId($id)
245
    {
246 2
        $list = (string)$this->getValue('PIPELINES_IDS');
247 2
        $hashes = preg_split('~\s+~', $list, -1, PREG_SPLIT_NO_EMPTY);
248 2
        $hashes = array_map('strtolower', /** @scrutinizer ignore-type */ $hashes);
249
250 2
        $idHash = md5($id);
251 2
        $hasId = in_array($idHash, $hashes, true);
252 2
        $hashes[] = $idHash;
253
254 2
        $this->vars['PIPELINES_ID'] = $id;
255 2
        $this->vars['PIPELINES_IDS'] = implode(' ', $hashes);
256
257 2
        return $hasId;
258
    }
259
260
    /**
261
     * $BITBUCKET_STEP_RUN_NUMBER can be inherited from environment and after
262
     * the first step did run is being reset to 1
263
     *
264
     * allows to test it for steps `BITBUCKET_STEP_RUN_NUMBER=2 pipelines --step 1-`
265
     */
266 1
    public function resetStepRunNumber()
267
    {
268 1
        $this->vars['BITBUCKET_STEP_RUN_NUMBER'] = '1';
269 1
    }
270
271
    /**
272
     * set PIPELINES_PROJECT_PATH
273
     *
274
     * can never be overwritten, must be set by pipelines itself for the
275
     * initial pipeline. will be taken over into each sub-pipeline.
276
     *
277
     * @param string $path absolute path to the project directory (deploy source path)
278
     *
279
     * @return void
280
     */
281 2
    public function setPipelinesProjectPath($path)
282
    {
283 2
        if (!LibFsPath::isAbsolute($path)) {
284 1
            throw new \InvalidArgumentException(sprintf('not an absolute path: "%s"', $path));
285
        }
286
287 1
        $this->setFirstPipelineVariable('PIPELINES_PROJECT_PATH', $path);
288 1
    }
289
290
    /**
291
     * @param string $option "-e" typically for Docker binary
292
     *
293
     * @return array of options (from $option) and values, ['-e', 'val1', '-e', 'val2', ...]
294
     */
295 13
    public function getArgs($option)
296
    {
297 13
        return Lib::merge(
298 13
            $this->collected,
299 13
            self::createArgVarDefinitions($option, $this->vars)
300
        );
301
    }
302
303
    /**
304
     * get a variables' value from the inherited
305
     * environment or null if not set
306
     *
307
     * @param string $name
308
     *
309
     * @return null|string
310
     */
311 1
    public function getInheritValue($name)
312
    {
313 1
        return isset($this->inherit[$name])
314 1
            ? $this->inherit[$name]
315 1
            : null;
316
    }
317
318
    /**
319
     * get a variables value or null if not set
320
     *
321
     * @param string $name
322
     *
323
     * @return null|string
324
     */
325 8
    public function getValue($name)
326
    {
327 8
        return isset($this->vars[$name])
328 7
            ? $this->vars[$name]
329 8
            : null;
330
    }
331
332
    /**
333
     * collect option arguments
334
     *
335
     * those options to be passed to docker client, normally -e,
336
     * --env and --env-file.
337
     *
338
     * @param Args $args
339
     * @param string|string[] $option
340
     *
341
     * @throws \InvalidArgumentException
342
     * @throws \Ktomk\Pipelines\Cli\ArgsException
343
     *
344
     * @return void
345
     */
346 2
    public function collect(Args $args, $option)
347
    {
348 2
        $collector = new Collector($args);
349 2
        $collector->collect($option);
350 2
        $this->collected = array_merge($this->collected, $collector->getArgs());
351
352 2
        $this->getResolver()->addArguments($collector);
353 2
    }
354
355
    /**
356
     * @param array $paths
357
     *
358
     * @throws \InvalidArgumentException
359
     *
360
     * @return void
361
     */
362 2
    public function collectFiles(array $paths)
363
    {
364 2
        $resolver = $this->getResolver();
365 2
        foreach ($paths as $path) {
366 2
            if ($resolver->addFileIfExists($path)) {
367 2
                $this->collected[] = '--env-file';
368 2
                $this->collected[] = $path;
369
            }
370
        }
371 2
    }
372
373
    /**
374
     * @return EnvResolver
375
     */
376 6
    public function getResolver()
377
    {
378 6
        if (null === $this->resolver) {
379 6
            $this->resolver = new EnvResolver($this->inherit);
380
        }
381
382 6
        return $this->resolver;
383
    }
384
385
    /**
386
     * get all variables collected so far
387
     *
388
     * @return array
389
     */
390 2
    public function getVariables()
391
    {
392 2
        return (array)$this->getResolver()->getVariables();
393
    }
394
395
    /**
396
     * @param array $inherit
397
     *
398
     * @return void
399
     */
400 24
    private function initInherit(array $inherit)
401
    {
402 24
        $this->inherit = array_filter($inherit, 'is_string');
403 24
    }
404
405
    /**
406
     * set an environment variable only if not yet set and in the first
407
     * pipeline.
408
     *
409
     * @param string $name
410
     * @param string $value
411
     *
412
     * @return void
413
     */
414 3
    private function setFirstPipelineVariable($name, $value)
415
    {
416 3
        if (isset($this->vars[$name])
417 3
            || !isset($this->vars['PIPELINES_ID'], $this->vars['PIPELINES_IDS'])
418 3
            || $this->vars['PIPELINES_IDS'] !== md5($this->vars['PIPELINES_ID'])
419
        ) {
420 3
            return;
421
        }
422
423 1
        $this->vars[$name] = $value;
424 1
    }
425
}
426