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