Runner::runSteps()   A
last analyzed

Complexity

Conditions 4
Paths 6

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 9
nc 6
nop 1
dl 0
loc 17
ccs 10
cts 10
cp 1
crap 4
rs 9.9666
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\Exec;
8
use Ktomk\Pipelines\Cli\Streams;
9
use Ktomk\Pipelines\File\Pipeline;
10
use Ktomk\Pipelines\File\Pipeline\Step;
11
12
/**
13
 * Pipeline runner with docker under the hood
14
 */
15
class Runner
16
{
17
    const STATUS_NO_STEPS = 1;
18
    const STATUS_RECURSION_DETECTED = 127;
19
20
    /**
21
     * @var RunOpts
22
     */
23
    private $runOpts;
24
25
    /**
26
     * @var Directories
27
     */
28
    private $directories;
29
30
    /**
31
     * @var Exec
32
     */
33
    private $exec;
34
35
    /**
36
     * @var Env
37
     */
38
    private $env;
39
40
    /**
41
     * @var Flags
42
     */
43
    private $flags;
44
45
    /**
46
     * @var Streams
47
     */
48
    private $streams;
49
50
    /**
51
     * Static factory method.
52
     *
53
     * The "ex" version of runner creation, moving creation out of the ctor itself.
54
     *
55
     * @param RunOpts $runOpts
56
     * @param Directories $directories source repository root directory based directories object
57
     * @param Exec $exec
58
     * @param Flags $flags [optional]
59
     * @param Env $env [optional]
60
     * @param Streams $streams [optional]
61
     *
62
     * @return Runner
63
     */
64 1
    public static function createEx(
65
        RunOpts $runOpts,
66
        Directories $directories,
67
        Exec $exec,
68
        Flags $flags = null,
69
        Env $env = null,
70
        Streams $streams = null
71
    ) {
72 1
        $flags = null === $flags ? new Flags() : $flags;
73 1
        $env = null === $env ? Env::createEx() : $env;
74 1
        $streams = null === $streams ? Streams::create() : $streams;
75
76 1
        return new self($runOpts, $directories, $exec, $flags, $env, $streams);
77
    }
78
79
    /**
80
     * Runner constructor.
81
     *
82
     * @param RunOpts $runOpts
83
     * @param Directories $directories source repository root directory based directories object
84
     * @param Exec $exec
85
     * @param Flags $flags
86
     * @param Env $env
87
     * @param Streams $streams
88
     */
89 7
    public function __construct(
90
        RunOpts $runOpts,
91
        Directories $directories,
92
        Exec $exec,
93
        Flags $flags,
94
        Env $env,
95
        Streams $streams
96
    ) {
97 7
        $this->runOpts = $runOpts;
98 7
        $this->directories = $directories;
99 7
        $this->exec = $exec;
100 7
        $this->flags = $flags;
101 7
        $this->env = $env;
102 7
        $this->streams = $streams;
103
    }
104
105
    /**
106
     * @param Pipeline $pipeline
107
     *
108
     * @throws \RuntimeException
109
     *
110
     * @return int status (as in exit status, 0 OK, !0 NOK)
111
     */
112 5
    public function run(Pipeline $pipeline)
113
    {
114 5
        $hasId = $this->env->setPipelinesId($pipeline->getId()); # TODO give Env an addPipeline() method (compare addReference)
115 5
        if ($hasId) {
116 1
            $this->streams->err(sprintf(
117
                "pipelines: won't start pipeline '%s'; pipeline inside pipelines recursion detected\n",
118 1
                $pipeline->getId()
119
            ));
120
121 1
            return self::STATUS_RECURSION_DETECTED;
122
        }
123
124 4
        $steps = $pipeline->getSteps()->getIterator();
125 4
        $steps->setNoManual($this->runOpts->isNoManual());
126 4
        list($status, $steps) = $this->runSteps($steps);
127
128 4
        if (0 === $status && $steps->isManual()) {
129 1
            $this->streams->err(sprintf(
130
                "pipelines: step #%d is manual. use `--steps %d-` to continue or `--no-manual` to override\n",
131 1
                $steps->getStepIndex() + 1,
132 1
                $steps->getStepIndex() + 1
133
            ));
134
        }
135
136 4
        return $status;
137
    }
138
139
    /**
140
     * @param Step $step
141
     *
142
     * @return null|int status (as in exit status, 0 OK, !0 NOK), null if the run operation failed
143
     */
144 2
    public function runStep(Step $step)
145
    {
146 2
        $stepRunner = new StepRunner($this);
147
148 2
        return $stepRunner->runStep($step);
149
    }
150
151
    /**
152
     * @return RunOpts
153
     */
154 2
    public function getRunOpts()
155
    {
156 2
        return $this->runOpts;
157
    }
158
159
    /**
160
     * @return Directories
161
     */
162 2
    public function getDirectories()
163
    {
164 2
        return $this->directories;
165
    }
166
167
    /**
168
     * @return Exec
169
     */
170 2
    public function getExec()
171
    {
172 2
        return $this->exec;
173
    }
174
175
    /**
176
     * @return Env
177
     */
178 2
    public function getEnv()
179
    {
180 2
        return $this->env;
181
    }
182
183
    /**
184
     * @return Flags
185
     */
186 2
    public function getFlags()
187
    {
188 2
        return $this->flags;
189
    }
190
191
    /**
192
     * @return Streams
193
     */
194 2
    public function getStreams()
195
    {
196 2
        return $this->streams;
197
    }
198
199
    /**
200
     * Project name (basename) in runner context (as used in running pipelines)
201
     *
202
     * @return string
203
     */
204 2
    public function getProject()
205
    {
206 2
        return $this->env->getValue('BITBUCKET_REPO_SLUG') ?: $this->directories->getName();
207
    }
208
209
    /**
210
     * Project directory
211
     *
212
     * @return string
213
     */
214 3
    public function getProjectDirectory()
215
    {
216 3
        return $this->getDirectories()->getProjectDirectory();
217
    }
218
219
    /**
220
     * Get Prefix
221
     *
222
     * The prefix is used when creating containers for the container name and
223
     * acts as name-spacing for pipelines resources (like containers).
224
     *
225
     * The default prefix is "pipelines", see --prefix option.
226
     *
227
     * @see Prefix::verify()
228
     *
229
     * @return string
230
     */
231 2
    public function getPrefix()
232
    {
233 2
        return $this->runOpts->getPrefix();
234
    }
235
236
    /**
237
     * @param Pipeline\StepsIterator $steps
238
     *
239
     * @return array
240
     * @psalm-return array{0: int, 1: Pipeline\StepsIterator}
241
     */
242 4
    private function runSteps(Pipeline\StepsIterator $steps)
243
    {
244 4
        foreach ($steps as $step) {
245 3
            $status = $this->runStep($step);
246 3
            if (0 !== $status) {
247 1
                break;
248
            }
249 2
            $this->env->resetStepRunNumber();
250
        }
251
252 4
        if (!isset($status)) {
253 1
            $this->streams->err("pipelines: pipeline with no step to execute\n");
254
255 1
            return array(self::STATUS_NO_STEPS, $steps);
256
        }
257
258 3
        return array((int)$status, $steps);
259
    }
260
}
261