Completed
Push — gittoolQuietOption ( 6c671f )
by Michael
03:30
created

GitToolCLI::cmd_git()   C

Complexity

Conditions 7
Paths 5

Size

Total Lines 29
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 21
nc 5
nop 3
dl 0
loc 29
rs 6.7272
c 0
b 0
f 0
1
#!/usr/bin/php
2
<?php
3
if(!defined('DOKU_INC')) define('DOKU_INC', realpath(dirname(__FILE__).'/../').'/');
4
define('NOSESSION', 1);
5
require_once(DOKU_INC.'inc/init.php');
6
7
/**
8
 * Easily manage DokuWiki git repositories
9
 *
10
 * @author Andreas Gohr <[email protected]>
11
 */
12
class GitToolCLI extends DokuCLI {
13
14
    /**
15
     * Register options and arguments on the given $options object
16
     *
17
     * @param DokuCLI_Options $options
18
     * @return void
19
     */
20
    protected function setup(DokuCLI_Options $options) {
21
        $options->setHelp(
22
            "Manage git repositories for DokuWiki and its plugins and templates.\n\n".
23
            "$> ./bin/gittool.php clone gallery template:ach\n".
24
            "$> ./bin/gittool.php repos\n".
25
            "$> ./bin/gittool.php origin -v"
26
        );
27
28
        $options->registerArgument(
29
            'command',
30
            'Command to execute. See below',
31
            true
32
        );
33
34
        $options->registerOption(
35
            'quiet',
36
            'If specified, show less cli boilerplate when running unknown commands',
37
            'q'
38
        );
39
40
        $options->registerCommand(
41
            'clone',
42
            'Tries to install a known plugin or template (prefix with template:) via git. Uses the DokuWiki.org '.
43
            'plugin repository to find the proper git repository. Multiple extensions can be given as parameters'
44
        );
45
        $options->registerArgument(
46
            'extension',
47
            'name of the extension to install, prefix with \'template:\' for templates',
48
            true,
49
            'clone'
50
        );
51
52
        $options->registerCommand(
53
            'install',
54
            'The same as clone, but when no git source repository can be found, the extension is installed via '.
55
            'download'
56
        );
57
        $options->registerArgument(
58
            'extension',
59
            'name of the extension to install, prefix with \'template:\' for templates',
60
            true,
61
            'install'
62
        );
63
64
        $options->registerCommand(
65
            'repos',
66
            'Lists all git repositories found in this DokuWiki installation'
67
        );
68
69
        $options->registerCommand(
70
            '*',
71
            'Any unknown commands are assumed to be arguments to git and will be executed in all repositories '.
72
            'found within this DokuWiki installation'
73
        );
74
    }
75
76
    /**
77
     * Your main program
78
     *
79
     * Arguments and options have been parsed when this is run
80
     *
81
     * @param DokuCLI_Options $options
82
     * @return void
83
     */
84
    protected function main(DokuCLI_Options $options) {
85
        $command = $options->getCmd();
86
        if(!$command) $command = array_shift($options->args);
87
88
        switch($command) {
89
            case '':
90
                echo $options->help();
91
                break;
92
            case 'clone':
93
                $this->cmd_clone($options->args);
94
                break;
95
            case 'install':
96
                $this->cmd_install($options->args);
97
                break;
98
            case 'repo':
99
            case 'repos':
100
                $this->cmd_repos();
101
                break;
102
            default:
103
                $this->cmd_git($command, $options->args, $options->getOpt('quiet'));
104
        }
105
    }
106
107
    /**
108
     * Tries to install the given extensions using git clone
109
     *
110
     * @param array      $extensions
111
     */
112
    public function cmd_clone($extensions) {
113
        $errors    = array();
114
        $succeeded = array();
115
116
        foreach($extensions as $ext) {
117
            $repo = $this->getSourceRepo($ext);
118
119
            if(!$repo) {
120
                $this->error("could not find a repository for $ext");
121
                $errors[] = $ext;
122
            } else {
123
                if($this->cloneExtension($ext, $repo)) {
124
                    $succeeded[] = $ext;
125
                } else {
126
                    $errors[] = $ext;
127
                }
128
            }
129
        }
130
131
        echo "\n";
132
        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...
133
        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...
134
    }
135
136
    /**
137
     * Tries to install the given extensions using git clone with fallback to install
138
     *
139
     * @param array      $extensions
140
     */
141
    public function cmd_install($extensions) {
142
        $errors    = array();
143
        $succeeded = array();
144
145
        foreach($extensions as $ext) {
146
            $repo = $this->getSourceRepo($ext);
147
148
            if(!$repo) {
149
                $this->info("could not find a repository for $ext");
150
                if($this->downloadExtension($ext)) {
151
                    $succeeded[] = $ext;
152
                } else {
153
                    $errors[] = $ext;
154
                }
155
            } else {
156
                if($this->cloneExtension($ext, $repo)) {
157
                    $succeeded[] = $ext;
158
                } else {
159
                    $errors[] = $ext;
160
                }
161
            }
162
        }
163
164
        echo "\n";
165
        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...
166
        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...
167
    }
168
169
    /**
170
     * Executes the given git command in every repository
171
     *
172
     * @param $cmd
173
     * @param $arg
174
     */
175
    public function cmd_git($cmd, $arg, $quiet) {
176
        $repos = $this->findRepos();
177
178
        $shell = array_merge(array('git', $cmd), $arg);
179
        $shell = array_map('escapeshellarg', $shell);
180
        $shell = join(' ', $shell);
181
182
        foreach($repos as $repo) {
183
            if(!@chdir($repo)) {
184
                $this->error("Could not change into $repo");
185
                continue;
186
            }
187
188
            $ret = 0;
189
            $output = array();
190
            exec($shell, $output, $ret);
191
            if (!$quiet || $output || $ret !== 0) {
192
                echo "\n";
193
                $this->info("executing $shell in $repo");
194
                echo implode("\n",$output);
195
                echo "\n";
196
                if($ret === 0) {
197
                    $this->success("git succeeded in $repo");
198
                } else {
199
                    $this->error("git failed in $repo");
200
                }
201
            }
202
        }
203
    }
204
205
    /**
206
     * Simply lists the repositories
207
     */
208
    public function cmd_repos() {
209
        $repos = $this->findRepos();
210
        foreach($repos as $repo) {
211
            echo "$repo\n";
212
        }
213
    }
214
215
    /**
216
     * Install extension from the given download URL
217
     *
218
     * @param string $ext
219
     * @return bool|null
220
     */
221
    private function downloadExtension($ext) {
222
        /** @var helper_plugin_extension_extension $plugin */
223
        $plugin = plugin_load('helper', 'extension_extension');
224
        if(!$ext) die("extension plugin not available, can't continue");
225
226
        $plugin->setExtension($ext);
227
228
        $url = $plugin->getDownloadURL();
229
        if(!$url) {
230
            $this->error("no download URL for $ext");
231
            return false;
232
        }
233
234
        $ok = false;
235
        try {
236
            $this->info("installing $ext via download from $url");
237
            $ok = $plugin->installFromURL($url);
238
        } catch(Exception $e) {
239
            $this->error($e->getMessage());
240
        }
241
242
        if($ok) {
243
            $this->success("installed $ext via download");
244
            return true;
245
        } else {
246
            $this->success("failed to install $ext via download");
247
            return false;
248
        }
249
    }
250
251
    /**
252
     * Clones the extension from the given repository
253
     *
254
     * @param string $ext
255
     * @param string $repo
256
     * @return bool
257
     */
258
    private function cloneExtension($ext, $repo) {
259
        if(substr($ext, 0, 9) == 'template:') {
260
            $target = fullpath(tpl_incdir().'../'.substr($ext, 9));
261
        } else {
262
            $target = DOKU_PLUGIN.$ext;
263
        }
264
265
        $this->info("cloning $ext from $repo to $target");
266
        $ret = 0;
267
        system("git clone $repo $target", $ret);
268
        if($ret === 0) {
269
            $this->success("cloning of $ext succeeded");
270
            return true;
271
        } else {
272
            $this->error("cloning of $ext failed");
273
            return false;
274
        }
275
    }
276
277
    /**
278
     * Returns all git repositories in this DokuWiki install
279
     *
280
     * Looks in root, template and plugin directories only.
281
     *
282
     * @return array
283
     */
284
    private function findRepos() {
285
        $this->info('Looking for .git directories');
286
        $data = array_merge(
287
            glob(DOKU_INC.'.git', GLOB_ONLYDIR),
288
            glob(DOKU_PLUGIN.'*/.git', GLOB_ONLYDIR),
289
            glob(fullpath(tpl_incdir().'../').'/*/.git', GLOB_ONLYDIR)
290
        );
291
292
        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...
293
            $this->error('Found no .git directories');
294
        } else {
295
            $this->success('Found '.count($data).' .git directories');
296
        }
297
        $data = array_map('fullpath', array_map('dirname', $data));
298
        return $data;
299
    }
300
301
    /**
302
     * Returns the repository for the given extension
303
     *
304
     * @param $extension
305
     * @return false|string
306
     */
307
    private function getSourceRepo($extension) {
308
        /** @var helper_plugin_extension_extension $ext */
309
        $ext = plugin_load('helper', 'extension_extension');
310
        if(!$ext) die("extension plugin not available, can't continue");
311
312
        $ext->setExtension($extension);
313
314
        $repourl = $ext->getSourcerepoURL();
315
        if(!$repourl) return false;
316
317
        // match github repos
318
        if(preg_match('/github\.com\/([^\/]+)\/([^\/]+)/i', $repourl, $m)) {
319
            $user = $m[1];
320
            $repo = $m[2];
321
            return 'https://github.com/'.$user.'/'.$repo.'.git';
322
        }
323
324
        // match gitorious repos
325
        if(preg_match('/gitorious.org\/([^\/]+)\/([^\/]+)?/i', $repourl, $m)) {
326
            $user = $m[1];
327
            $repo = $m[2];
328
            if(!$repo) $repo = $user;
329
330
            return 'https://git.gitorious.org/'.$user.'/'.$repo.'.git';
331
        }
332
333
        // match bitbucket repos - most people seem to use mercurial there though
334
        if(preg_match('/bitbucket\.org\/([^\/]+)\/([^\/]+)/i', $repourl, $m)) {
335
            $user = $m[1];
336
            $repo = $m[2];
337
            return 'https://bitbucket.org/'.$user.'/'.$repo.'.git';
338
        }
339
340
        return false;
341
    }
342
}
343
344
// Main
345
$cli = new GitToolCLI();
346
$cli->run();
347