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