Completed
Push — psr2 ( c68e26...1a953a )
by Andreas
08:17 queued 04:11
created

GitToolCLI::cmdInstall()   C

Complexity

Conditions 7
Paths 20

Size

Total Lines 27
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 19
nc 20
nop 1
dl 0
loc 27
rs 6.7272
c 0
b 0
f 0
1
#!/usr/bin/php
2
<?php
3
4
use splitbrain\phpcli\CLI;
5
use splitbrain\phpcli\Options;
6
7
if(!defined('DOKU_INC')) define('DOKU_INC', realpath(dirname(__FILE__) . '/../') . '/');
8
define('NOSESSION', 1);
9
require_once(DOKU_INC . 'inc/init.php');
10
11
/**
12
 * Easily manage DokuWiki git repositories
13
 *
14
 * @author Andreas Gohr <[email protected]>
15
 */
16
class GitToolCLI extends CLI {
17
18
    /**
19
     * Register options and arguments on the given $options object
20
     *
21
     * @param Options $options
22
     * @return void
23
     */
24
    protected function setup(Options $options) {
25
        $options->setHelp(
26
            "Manage git repositories for DokuWiki and its plugins and templates.\n\n" .
27
            "$> ./bin/gittool.php clone gallery template:ach\n" .
28
            "$> ./bin/gittool.php repos\n" .
29
            "$> ./bin/gittool.php origin -v"
30
        );
31
32
        $options->registerArgument(
33
            'command',
34
            'Command to execute. See below',
35
            true
36
        );
37
38
        $options->registerCommand(
39
            'clone',
40
            'Tries to install a known plugin or template (prefix with template:) via git. Uses the DokuWiki.org ' .
41
            'plugin repository to find the proper git repository. Multiple extensions can be given as parameters'
42
        );
43
        $options->registerArgument(
44
            'extension',
45
            'name of the extension to install, prefix with \'template:\' for templates',
46
            true,
47
            'clone'
48
        );
49
50
        $options->registerCommand(
51
            'install',
52
            'The same as clone, but when no git source repository can be found, the extension is installed via ' .
53
            'download'
54
        );
55
        $options->registerArgument(
56
            'extension',
57
            'name of the extension to install, prefix with \'template:\' for templates',
58
            true,
59
            'install'
60
        );
61
62
        $options->registerCommand(
63
            'repos',
64
            'Lists all git repositories found in this DokuWiki installation'
65
        );
66
67
        $options->registerCommand(
68
            '*',
69
            'Any unknown commands are assumed to be arguments to git and will be executed in all repositories ' .
70
            'found within this DokuWiki installation'
71
        );
72
    }
73
74
    /**
75
     * Your main program
76
     *
77
     * Arguments and options have been parsed when this is run
78
     *
79
     * @param Options $options
80
     * @return void
81
     */
82
    protected function main(Options $options) {
83
        $command = $options->getCmd();
84
        $args = $options->getArgs();
85
        if(!$command) $command = array_shift($args);
86
87
        switch($command) {
88
            case '':
89
                echo $options->help();
90
                break;
91
            case 'clone':
92
                $this->cmdClone($args);
93
                break;
94
            case 'install':
95
                $this->cmdInstall($args);
96
                break;
97
            case 'repo':
98
            case 'repos':
99
                $this->cmdRepos();
100
                break;
101
            default:
102
                $this->cmdGit($command, $args);
103
        }
104
    }
105
106
    /**
107
     * Tries to install the given extensions using git clone
108
     *
109
     * @param array $extensions
110
     */
111
    public function cmdClone($extensions) {
112
        $errors = array();
113
        $succeeded = array();
114
115
        foreach($extensions as $ext) {
116
            $repo = $this->getSourceRepo($ext);
117
118
            if(!$repo) {
119
                $this->error("could not find a repository for $ext");
120
                $errors[] = $ext;
121
            } else {
122
                if($this->cloneExtension($ext, $repo)) {
123
                    $succeeded[] = $ext;
124
                } else {
125
                    $errors[] = $ext;
126
                }
127
            }
128
        }
129
130
        echo "\n";
131
        if($succeeded) $this->success('successfully cloned the following extensions: ' . join(', ', $succeeded));
0 ignored issues
show
Bug Best Practice introduced by
The expression $succeeded of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
132
        if($errors) $this->error('failed to clone the following extensions: ' . join(', ', $errors));
0 ignored issues
show
Bug Best Practice introduced by
The expression $errors of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
133
    }
134
135
    /**
136
     * Tries to install the given extensions using git clone with fallback to install
137
     *
138
     * @param array $extensions
139
     */
140
    public function cmdInstall($extensions) {
141
        $errors = array();
142
        $succeeded = array();
143
144
        foreach($extensions as $ext) {
145
            $repo = $this->getSourceRepo($ext);
146
147
            if(!$repo) {
148
                $this->info("could not find a repository for $ext");
149
                if($this->downloadExtension($ext)) {
150
                    $succeeded[] = $ext;
151
                } else {
152
                    $errors[] = $ext;
153
                }
154
            } else {
155
                if($this->cloneExtension($ext, $repo)) {
156
                    $succeeded[] = $ext;
157
                } else {
158
                    $errors[] = $ext;
159
                }
160
            }
161
        }
162
163
        echo "\n";
164
        if($succeeded) $this->success('successfully installed the following extensions: ' . join(', ', $succeeded));
0 ignored issues
show
Bug Best Practice introduced by
The expression $succeeded of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
165
        if($errors) $this->error('failed to install the following extensions: ' . join(', ', $errors));
0 ignored issues
show
Bug Best Practice introduced by
The expression $errors of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
166
    }
167
168
    /**
169
     * Executes the given git command in every repository
170
     *
171
     * @param $cmd
172
     * @param $arg
173
     */
174
    public function cmdGit($cmd, $arg) {
175
        $repos = $this->findRepos();
176
177
        $shell = array_merge(array('git', $cmd), $arg);
178
        $shell = array_map('escapeshellarg', $shell);
179
        $shell = join(' ', $shell);
180
181
        foreach($repos as $repo) {
182
            if(!@chdir($repo)) {
183
                $this->error("Could not change into $repo");
184
                continue;
185
            }
186
187
            $this->info("executing $shell in $repo");
188
            $ret = 0;
189
            system($shell, $ret);
190
191
            if($ret == 0) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $ret of type integer|null to 0; this is ambiguous as not only 0 == 0 is true, but null == 0 is true, too. Consider using a strict comparison ===.
Loading history...
192
                $this->success("git succeeded in $repo");
193
            } else {
194
                $this->error("git failed in $repo");
195
            }
196
        }
197
    }
198
199
    /**
200
     * Simply lists the repositories
201
     */
202
    public function cmdRepos() {
203
        $repos = $this->findRepos();
204
        foreach($repos as $repo) {
205
            echo "$repo\n";
206
        }
207
    }
208
209
    /**
210
     * Install extension from the given download URL
211
     *
212
     * @param string $ext
213
     * @return bool|null
214
     */
215
    private function downloadExtension($ext) {
216
        /** @var helper_plugin_extension_extension $plugin */
217
        $plugin = plugin_load('helper', 'extension_extension');
218
        if(!$ext) die("extension plugin not available, can't continue");
219
220
        $plugin->setExtension($ext);
221
222
        $url = $plugin->getDownloadURL();
223
        if(!$url) {
224
            $this->error("no download URL for $ext");
225
            return false;
226
        }
227
228
        $ok = false;
229
        try {
230
            $this->info("installing $ext via download from $url");
231
            $ok = $plugin->installFromURL($url);
232
        } catch(Exception $e) {
233
            $this->error($e->getMessage());
234
        }
235
236
        if($ok) {
237
            $this->success("installed $ext via download");
238
            return true;
239
        } else {
240
            $this->success("failed to install $ext via download");
241
            return false;
242
        }
243
    }
244
245
    /**
246
     * Clones the extension from the given repository
247
     *
248
     * @param string $ext
249
     * @param string $repo
250
     * @return bool
251
     */
252
    private function cloneExtension($ext, $repo) {
253
        if(substr($ext, 0, 9) == 'template:') {
254
            $target = fullpath(tpl_incdir() . '../' . substr($ext, 9));
255
        } else {
256
            $target = DOKU_PLUGIN . $ext;
257
        }
258
259
        $this->info("cloning $ext from $repo to $target");
260
        $ret = 0;
261
        system("git clone $repo $target", $ret);
262
        if($ret === 0) {
263
            $this->success("cloning of $ext succeeded");
264
            return true;
265
        } else {
266
            $this->error("cloning of $ext failed");
267
            return false;
268
        }
269
    }
270
271
    /**
272
     * Returns all git repositories in this DokuWiki install
273
     *
274
     * Looks in root, template and plugin directories only.
275
     *
276
     * @return array
277
     */
278
    private function findRepos() {
279
        $this->info('Looking for .git directories');
280
        $data = array_merge(
281
            glob(DOKU_INC . '.git', GLOB_ONLYDIR),
282
            glob(DOKU_PLUGIN . '*/.git', GLOB_ONLYDIR),
283
            glob(fullpath(tpl_incdir() . '../') . '/*/.git', GLOB_ONLYDIR)
284
        );
285
286
        if(!$data) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $data of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
287
            $this->error('Found no .git directories');
288
        } else {
289
            $this->success('Found ' . count($data) . ' .git directories');
290
        }
291
        $data = array_map('fullpath', array_map('dirname', $data));
292
        return $data;
293
    }
294
295
    /**
296
     * Returns the repository for the given extension
297
     *
298
     * @param $extension
299
     * @return false|string
300
     */
301
    private function getSourceRepo($extension) {
302
        /** @var helper_plugin_extension_extension $ext */
303
        $ext = plugin_load('helper', 'extension_extension');
304
        if(!$ext) die("extension plugin not available, can't continue");
305
306
        $ext->setExtension($extension);
307
308
        $repourl = $ext->getSourcerepoURL();
309
        if(!$repourl) return false;
310
311
        // match github repos
312
        if(preg_match('/github\.com\/([^\/]+)\/([^\/]+)/i', $repourl, $m)) {
313
            $user = $m[1];
314
            $repo = $m[2];
315
            return 'https://github.com/' . $user . '/' . $repo . '.git';
316
        }
317
318
        // match gitorious repos
319
        if(preg_match('/gitorious.org\/([^\/]+)\/([^\/]+)?/i', $repourl, $m)) {
320
            $user = $m[1];
321
            $repo = $m[2];
322
            if(!$repo) $repo = $user;
323
324
            return 'https://git.gitorious.org/' . $user . '/' . $repo . '.git';
325
        }
326
327
        // match bitbucket repos - most people seem to use mercurial there though
328
        if(preg_match('/bitbucket\.org\/([^\/]+)\/([^\/]+)/i', $repourl, $m)) {
329
            $user = $m[1];
330
            $repo = $m[2];
331
            return 'https://bitbucket.org/' . $user . '/' . $repo . '.git';
332
        }
333
334
        return false;
335
    }
336
}
337
338
// Main
339
$cli = new GitToolCLI();
340
$cli->run();
341