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