Passed
Push — master ( 9e5fe0...88d9d6 )
by Tom
04:35
created

RunnerOptions::createRepository()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 3
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 5
ccs 4
cts 4
cp 1
crap 1
rs 10
1
<?php
2
3
/* this file is part of pipelines */
4
5
namespace Ktomk\Pipelines\Utility;
6
7
use Ktomk\Pipelines\Cli\Args;
8
use Ktomk\Pipelines\Cli\ArgsException;
9
use Ktomk\Pipelines\Cli\Exec;
10
use Ktomk\Pipelines\Cli\Streams;
11
use Ktomk\Pipelines\Lib;
12
use Ktomk\Pipelines\Project;
13
use Ktomk\Pipelines\Runner\Directories;
14
use Ktomk\Pipelines\Runner\Docker\Binary\Repository;
15
use Ktomk\Pipelines\Runner\RunOpts;
16
17
/**
18
 * aggregated args parser for RunOpts / runner options
19
 *
20
 * @package Ktomk\Pipelines\Utility\Args
21
 */
22
class RunnerOptions
23
{
24
    /**
25
     * @var Args
26
     */
27
    private $args;
28
29
    /**
30
     * @var Streams
31
     */
32
    private $streams;
33
34
    /**
35
     * @var Exec
36
     */
37
    private $exec;
38
39
    /**
40
     * @param Args $args
41
     * @param Streams $streams
42
     *
43
     * @return RunnerOptions
44
     */
45 4
    public static function bind(Args $args, Streams $streams)
46
    {
47 4
        return new self($args, $streams);
48
    }
49
50
    /**
51
     * the repository used for listing and validation
52
     *
53
     * @return Repository
54
     */
55 3
    public static function createRepository()
56
    {
57 3
        return Repository::create(
58 3
            new Exec(),
59 3
            new Directories(Lib::env($_SERVER), new Project('fake'))
60
        );
61
    }
62
63
    /**
64
     * list all statically available docker client package names
65
     * that ship w/ pipelines
66
     *
67
     * @param Streams $streams
68
     *
69
     * @return void
70
     */
71 1
    public static function listPackages(Streams $streams)
72
    {
73 1
        $list = self::createRepository()->listPackages();
74
75 1
        $streams->out(implode("\n", $list));
76 1
        $streams->out("\n");
77 1
    }
78
79
    /**
80
     * RunnerOptions constructor,
81
     *
82
     * @param Args $args
83
     * @param Streams $streams
84
     * @param Exec $exec
85
     */
86 5
    public function __construct(Args $args, Streams $streams, Exec $exec = null)
87
    {
88 5
        $this->args = $args;
89 5
        $this->streams = $streams;
90 5
        $this->exec = $exec ?: new Exec();
91 5
    }
92
93
    /**
94
     * @throws ArgsException
95
     * @throws StatusException
96
     *
97
     * @return RunOpts
98
     */
99 5
    public function run()
100
    {
101 5
        $runOpts = RunOpts::create(
102 5
            null,
103 5
            null,
104 5
            ConfigOptions::bind($this->args)->run()
105
        );
106
107 5
        $this->parse($this->args, $runOpts);
108
109 2
        return $runOpts;
110
    }
111
112
    /**
113
     * Parse keep arguments
114
     *
115
     * @param Args $args
116
     * @param RunOpts $runOpts
117
     *
118
     * @throws ArgsException
119
     * @throws StatusException
120
     *
121
     * @return void
122
     */
123 5
    public function parse(Args $args, RunOpts $runOpts)
124
    {
125
        // FIXME(tk): \Ktomk\Pipelines\Value\Prefix::verify
126 5
        $runOpts->setPrefix($this->parsePrefix($args));
127
128 4
        $runOpts->setBinaryPackage($this->parseDockerClient($args));
129
130 3
        $this->parseDockerClientListPackages($args);
131
132 2
        $runOpts->setSteps($args->getOptionArgument(array('step', 'steps')));
133
134 2
        $runOpts->setNoManual($args->hasOption('no-manual'));
135
136 2
        $runOpts->setUser($this->parseUser($args));
137
138 2
        $runOpts->setSsh($args->hasOption('ssh') ?: null);
139 2
    }
140
141
    /**
142
     * @param Args $args
143
     *
144
     * @throws ArgsException
145
     *
146
     * @return null|string
147
     */
148 2
    private function parseUser(Args $args)
149
    {
150 2
        $result = $args->getOptionOptionalArgument('user', true);
151 2
        if (null === $result || is_string($result)) {
152 1
            return $result;
153
        }
154
155 1
        $exec = $this->exec ?: new Exec();
156 1
        $status = $exec->capture('printf "%d:%d" "$(id -u)" "$(id -g)"', array(), $out, $err);
157 1
        if (0 !== $status || '' !== $err) {
158 1
            throw new ArgsException(
159 1
                sprintf(
160 1
                    '--user internal error to resolve id -u / id -g: %d%s',
161
                    $status,
162 1
                    $err ? " : ${err}" : ''
163
                )
164
            );
165
        }
166
167
        return $out;
168
    }
169
170
    /**
171
     * @param Args $args
172
     *
173
     * @throws ArgsException
174
     *
175
     * @return string
176
     */
177
    private function parseDockerClient(Args $args)
178
    {
179 4
        $default = Repository::PKG_INTEGRATE;
180 4
        $binaryClient = $args->getStringOptionArgument('docker-client', $default);
181 4
        if ($binaryClient !== $default) {
182 2
            $repository = self::createRepository();
183
184
            try {
185 2
                $repository->resolve($binaryClient);
186 1
            } catch (\InvalidArgumentException $ex) {
187 1
                $message = "--docker-client needs a valid package name, file or docker client binary path; '${binaryClient}' given";
188 1
                $message .= "\n  docker client binary packages shipping w/ pipelines:";
189 1
                $message .= "\n    - " . implode("\n    - ", $repository->listPackages());
190
191 1
                throw new ArgsException($message);
192
            }
193
        }
194
195 3
        return $binaryClient;
196
    }
197
198
    /**
199
     * @param Args $args
200
     *
201
     * @throws StatusException
202
     *
203
     * @return void
204
     */
205
    private function parseDockerClientListPackages(Args $args)
206
    {
207 3
        if (!$args->hasOption('docker-client-pkgs')) {
208 2
            return;
209
        }
210
211 1
        self::listPackages($this->streams);
212
213 1
        throw new StatusException('', 0);
214
    }
215
216
    /**
217
     * @param Args $args
218
     *
219
     * @throws ArgsException
220
     *
221
     * @return string
222
     */
223
    private function parsePrefix(Args $args)
224
    {
225 5
        $prefix = $args->getStringOptionArgument('prefix', App::UTILITY_NAME);
226 5
        if (!preg_match('~^[a-z]{3,}$~', $prefix)) {
227 1
            throw new ArgsException(sprintf("invalid prefix: '%s'", $prefix));
228
        }
229
230 4
        return $prefix;
231
    }
232
}
233