Passed
Push — master ( 800fb3...1ce7d0 )
by Tom
04:22
created

Env::createArgVarDefinitions()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

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