Passed
Push — test ( 52bb81...4c4f16 )
by Tom
02:50
created

Env::setFirstPipelineVariable()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 4

Importance

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