1c39ae2c9SAndreas Gohr#!/usr/bin/php 2c39ae2c9SAndreas Gohr<?php 3c39ae2c9SAndreas Gohrif(!defined('DOKU_INC')) define('DOKU_INC', realpath(dirname(__FILE__).'/../').'/'); 4c39ae2c9SAndreas Gohrdefine('NOSESSION', 1); 5c39ae2c9SAndreas Gohrrequire_once(DOKU_INC.'inc/init.php'); 6c39ae2c9SAndreas Gohr 7c39ae2c9SAndreas Gohr/** 8c39ae2c9SAndreas Gohr * Easily manage DokuWiki git repositories 9c39ae2c9SAndreas Gohr * 10c39ae2c9SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 11c39ae2c9SAndreas Gohr */ 129fb66494SAndreas Gohrclass GitToolCLI extends DokuCLI { 13c39ae2c9SAndreas Gohr 149fb66494SAndreas Gohr /** 159fb66494SAndreas Gohr * Register options and arguments on the given $options object 169fb66494SAndreas Gohr * 179fb66494SAndreas Gohr * @param DokuCLI_Options $options 189fb66494SAndreas Gohr * @return void 199fb66494SAndreas Gohr */ 209fb66494SAndreas Gohr protected function setup(DokuCLI_Options $options) { 219fb66494SAndreas Gohr $options->setHelp( 229fb66494SAndreas Gohr "Manage git repositories for DokuWiki and its plugins and templates.\n\n". 239fb66494SAndreas Gohr "$> ./bin/gittool.php clone gallery template:ach\n". 249fb66494SAndreas Gohr "$> ./bin/gittool.php repos\n". 25ae1ce4a6SAndreas Gohr "$> ./bin/gittool.php origin -v" 269fb66494SAndreas Gohr ); 27c39ae2c9SAndreas Gohr 289fb66494SAndreas Gohr $options->registerArgument( 299fb66494SAndreas Gohr 'command', 309fb66494SAndreas Gohr 'Command to execute. See below', 319fb66494SAndreas Gohr true 329fb66494SAndreas Gohr ); 33c39ae2c9SAndreas Gohr 349fb66494SAndreas Gohr $options->registerCommand( 359fb66494SAndreas Gohr 'clone', 369fb66494SAndreas Gohr 'Tries to install a known plugin or template (prefix with template:) via git. Uses the DokuWiki.org '. 379fb66494SAndreas Gohr 'plugin repository to find the proper git repository. Multiple extensions can be given as parameters' 389fb66494SAndreas Gohr ); 399fb66494SAndreas Gohr $options->registerArgument( 409fb66494SAndreas Gohr 'extension', 419fb66494SAndreas Gohr 'name of the extension to install, prefix with \'template:\' for templates', 429fb66494SAndreas Gohr true, 439fb66494SAndreas Gohr 'clone' 449fb66494SAndreas Gohr ); 45c39ae2c9SAndreas Gohr 469fb66494SAndreas Gohr $options->registerCommand( 479fb66494SAndreas Gohr 'install', 489fb66494SAndreas Gohr 'The same as clone, but when no git source repository can be found, the extension is installed via '. 499fb66494SAndreas Gohr 'download' 509fb66494SAndreas Gohr ); 519fb66494SAndreas Gohr $options->registerArgument( 529fb66494SAndreas Gohr 'extension', 539fb66494SAndreas Gohr 'name of the extension to install, prefix with \'template:\' for templates', 549fb66494SAndreas Gohr true, 559fb66494SAndreas Gohr 'install' 569fb66494SAndreas Gohr ); 57c39ae2c9SAndreas Gohr 589fb66494SAndreas Gohr $options->registerCommand( 599fb66494SAndreas Gohr 'repos', 609fb66494SAndreas Gohr 'Lists all git repositories found in this DokuWiki installation' 619fb66494SAndreas Gohr ); 62c39ae2c9SAndreas Gohr 639fb66494SAndreas Gohr $options->registerCommand( 649fb66494SAndreas Gohr '*', 659fb66494SAndreas Gohr 'Any unknown commands are assumed to be arguments to git and will be executed in all repositories '. 669fb66494SAndreas Gohr 'found within this DokuWiki installation' 679fb66494SAndreas Gohr ); 68c39ae2c9SAndreas Gohr } 69c39ae2c9SAndreas Gohr 70c39ae2c9SAndreas Gohr /** 719fb66494SAndreas Gohr * Your main program 729fb66494SAndreas Gohr * 739fb66494SAndreas Gohr * Arguments and options have been parsed when this is run 749fb66494SAndreas Gohr * 759fb66494SAndreas Gohr * @param DokuCLI_Options $options 769fb66494SAndreas Gohr * @return void 779fb66494SAndreas Gohr */ 789fb66494SAndreas Gohr protected function main(DokuCLI_Options $options) { 799fb66494SAndreas Gohr $command = $options->getCmd(); 809fb66494SAndreas Gohr if(!$command) $command = array_shift($options->args); 819fb66494SAndreas Gohr 829fb66494SAndreas Gohr switch($command) { 839fb66494SAndreas Gohr case '': 84*1c36b3d8SAndreas Gohr echo $options->help(); 859fb66494SAndreas Gohr break; 869fb66494SAndreas Gohr case 'clone': 879fb66494SAndreas Gohr $this->cmd_clone($options->args); 889fb66494SAndreas Gohr break; 899fb66494SAndreas Gohr case 'install': 909fb66494SAndreas Gohr $this->cmd_install($options->args); 919fb66494SAndreas Gohr break; 929fb66494SAndreas Gohr case 'repo': 939fb66494SAndreas Gohr case 'repos': 949fb66494SAndreas Gohr $this->cmd_repos(); 959fb66494SAndreas Gohr break; 969fb66494SAndreas Gohr default: 979fb66494SAndreas Gohr $this->cmd_git($command, $options->args); 989fb66494SAndreas Gohr } 999fb66494SAndreas Gohr } 1009fb66494SAndreas Gohr 1019fb66494SAndreas Gohr /** 102c39ae2c9SAndreas Gohr * Tries to install the given extensions using git clone 103c39ae2c9SAndreas Gohr * 104c39ae2c9SAndreas Gohr * @param $extensions 105c39ae2c9SAndreas Gohr */ 106c39ae2c9SAndreas Gohr public function cmd_clone($extensions) { 107c39ae2c9SAndreas Gohr $errors = array(); 108c39ae2c9SAndreas Gohr $succeeded = array(); 109c39ae2c9SAndreas Gohr 110c39ae2c9SAndreas Gohr foreach($extensions as $ext) { 111c39ae2c9SAndreas Gohr $repo = $this->getSourceRepo($ext); 112c39ae2c9SAndreas Gohr 113c39ae2c9SAndreas Gohr if(!$repo) { 1149fb66494SAndreas Gohr $this->error("could not find a repository for $ext"); 115c39ae2c9SAndreas Gohr $errors[] = $ext; 116c39ae2c9SAndreas Gohr } else { 117c39ae2c9SAndreas Gohr if($this->cloneExtension($ext, $repo)) { 118c39ae2c9SAndreas Gohr $succeeded[] = $ext; 119c39ae2c9SAndreas Gohr } else { 120c39ae2c9SAndreas Gohr $errors[] = $ext; 121c39ae2c9SAndreas Gohr } 122c39ae2c9SAndreas Gohr } 123c39ae2c9SAndreas Gohr } 124c39ae2c9SAndreas Gohr 125c39ae2c9SAndreas Gohr echo "\n"; 1269fb66494SAndreas Gohr if($succeeded) $this->success('successfully cloned the following extensions: '.join(', ', $succeeded)); 1279fb66494SAndreas Gohr if($errors) $this->error('failed to clone the following extensions: '.join(', ', $errors)); 128c39ae2c9SAndreas Gohr } 129c39ae2c9SAndreas Gohr 130c39ae2c9SAndreas Gohr /** 131c39ae2c9SAndreas Gohr * Tries to install the given extensions using git clone with fallback to install 132c39ae2c9SAndreas Gohr * 133c39ae2c9SAndreas Gohr * @param $extensions 134c39ae2c9SAndreas Gohr */ 135c39ae2c9SAndreas Gohr public function cmd_install($extensions) { 136c39ae2c9SAndreas Gohr $errors = array(); 137c39ae2c9SAndreas Gohr $succeeded = array(); 138c39ae2c9SAndreas Gohr 139c39ae2c9SAndreas Gohr foreach($extensions as $ext) { 140c39ae2c9SAndreas Gohr $repo = $this->getSourceRepo($ext); 141c39ae2c9SAndreas Gohr 142c39ae2c9SAndreas Gohr if(!$repo) { 1439fb66494SAndreas Gohr $this->info("could not find a repository for $ext"); 144c39ae2c9SAndreas Gohr if($this->downloadExtension($ext)) { 145c39ae2c9SAndreas Gohr $succeeded[] = $ext; 146c39ae2c9SAndreas Gohr } else { 147c39ae2c9SAndreas Gohr $errors[] = $ext; 148c39ae2c9SAndreas Gohr } 149c39ae2c9SAndreas Gohr } else { 150c39ae2c9SAndreas Gohr if($this->cloneExtension($ext, $repo)) { 151c39ae2c9SAndreas Gohr $succeeded[] = $ext; 152c39ae2c9SAndreas Gohr } else { 153c39ae2c9SAndreas Gohr $errors[] = $ext; 154c39ae2c9SAndreas Gohr } 155c39ae2c9SAndreas Gohr } 156c39ae2c9SAndreas Gohr } 157c39ae2c9SAndreas Gohr 158c39ae2c9SAndreas Gohr echo "\n"; 1599fb66494SAndreas Gohr if($succeeded) $this->success('successfully installed the following extensions: '.join(', ', $succeeded)); 1609fb66494SAndreas Gohr if($errors) $this->error('failed to install the following extensions: '.join(', ', $errors)); 161c39ae2c9SAndreas Gohr } 162c39ae2c9SAndreas Gohr 163c39ae2c9SAndreas Gohr /** 164c39ae2c9SAndreas Gohr * Executes the given git command in every repository 165c39ae2c9SAndreas Gohr * 166c39ae2c9SAndreas Gohr * @param $cmd 167c39ae2c9SAndreas Gohr * @param $arg 168c39ae2c9SAndreas Gohr */ 169c39ae2c9SAndreas Gohr public function cmd_git($cmd, $arg) { 170c39ae2c9SAndreas Gohr $repos = $this->findRepos(); 171c39ae2c9SAndreas Gohr 172c39ae2c9SAndreas Gohr $shell = array_merge(array('git', $cmd), $arg); 173c39ae2c9SAndreas Gohr $shell = array_map('escapeshellarg', $shell); 174c39ae2c9SAndreas Gohr $shell = join(' ', $shell); 175c39ae2c9SAndreas Gohr 176c39ae2c9SAndreas Gohr foreach($repos as $repo) { 177c39ae2c9SAndreas Gohr if(!@chdir($repo)) { 1789fb66494SAndreas Gohr $this->error("Could not change into $repo"); 179c39ae2c9SAndreas Gohr continue; 180c39ae2c9SAndreas Gohr } 181c39ae2c9SAndreas Gohr 182c39ae2c9SAndreas Gohr echo "\n"; 1839fb66494SAndreas Gohr $this->info("executing $shell in $repo"); 184c39ae2c9SAndreas Gohr $ret = 0; 185c39ae2c9SAndreas Gohr system($shell, $ret); 186c39ae2c9SAndreas Gohr 187c39ae2c9SAndreas Gohr if($ret == 0) { 1889fb66494SAndreas Gohr $this->success("git succeeded in $repo"); 189c39ae2c9SAndreas Gohr } else { 1909fb66494SAndreas Gohr $this->error("git failed in $repo"); 191c39ae2c9SAndreas Gohr } 192c39ae2c9SAndreas Gohr } 193c39ae2c9SAndreas Gohr } 194c39ae2c9SAndreas Gohr 195c39ae2c9SAndreas Gohr /** 196c39ae2c9SAndreas Gohr * Simply lists the repositories 197c39ae2c9SAndreas Gohr */ 198c39ae2c9SAndreas Gohr public function cmd_repos() { 199c39ae2c9SAndreas Gohr $repos = $this->findRepos(); 200c39ae2c9SAndreas Gohr foreach($repos as $repo) { 201c39ae2c9SAndreas Gohr echo "$repo\n"; 202c39ae2c9SAndreas Gohr } 203c39ae2c9SAndreas Gohr } 204c39ae2c9SAndreas Gohr 205c39ae2c9SAndreas Gohr /** 206c39ae2c9SAndreas Gohr * Install extension from the given download URL 207c39ae2c9SAndreas Gohr * 208c39ae2c9SAndreas Gohr * @param string $ext 209c39ae2c9SAndreas Gohr * @return bool 210c39ae2c9SAndreas Gohr */ 211c39ae2c9SAndreas Gohr private function downloadExtension($ext) { 212c39ae2c9SAndreas Gohr /** @var helper_plugin_extension_extension $plugin */ 213c39ae2c9SAndreas Gohr $plugin = plugin_load('helper', 'extension_extension'); 214c39ae2c9SAndreas Gohr if(!$ext) die("extension plugin not available, can't continue"); 215c39ae2c9SAndreas Gohr $plugin->setExtension($ext); 216c39ae2c9SAndreas Gohr 217c39ae2c9SAndreas Gohr $url = $plugin->getDownloadURL(); 218c39ae2c9SAndreas Gohr if(!$url) { 2199fb66494SAndreas Gohr $this->error("no download URL for $ext"); 220c39ae2c9SAndreas Gohr return false; 221c39ae2c9SAndreas Gohr } 222c39ae2c9SAndreas Gohr 223c39ae2c9SAndreas Gohr $ok = false; 224c39ae2c9SAndreas Gohr try { 2259fb66494SAndreas Gohr $this->info("installing $ext via download from $url"); 226c39ae2c9SAndreas Gohr $ok = $plugin->installFromURL($url); 227c39ae2c9SAndreas Gohr } catch(Exception $e) { 2289fb66494SAndreas Gohr $this->error($e->getMessage()); 229c39ae2c9SAndreas Gohr } 230c39ae2c9SAndreas Gohr 231c39ae2c9SAndreas Gohr if($ok) { 2329fb66494SAndreas Gohr $this->success("installed $ext via download"); 233c39ae2c9SAndreas Gohr return true; 234c39ae2c9SAndreas Gohr } else { 2359fb66494SAndreas Gohr $this->success("failed to install $ext via download"); 236c39ae2c9SAndreas Gohr return false; 237c39ae2c9SAndreas Gohr } 238c39ae2c9SAndreas Gohr } 239c39ae2c9SAndreas Gohr 240c39ae2c9SAndreas Gohr /** 241c39ae2c9SAndreas Gohr * Clones the extension from the given repository 242c39ae2c9SAndreas Gohr * 243c39ae2c9SAndreas Gohr * @param string $ext 244c39ae2c9SAndreas Gohr * @param string $repo 245c39ae2c9SAndreas Gohr * @return bool 246c39ae2c9SAndreas Gohr */ 247c39ae2c9SAndreas Gohr private function cloneExtension($ext, $repo) { 248c39ae2c9SAndreas Gohr if(substr($ext, 0, 9) == 'template:') { 249c39ae2c9SAndreas Gohr $target = fullpath(tpl_incdir().'../'.substr($ext, 9)); 250c39ae2c9SAndreas Gohr } else { 251c39ae2c9SAndreas Gohr $target = DOKU_PLUGIN.$ext; 252c39ae2c9SAndreas Gohr } 253c39ae2c9SAndreas Gohr 2549fb66494SAndreas Gohr $this->info("cloning $ext from $repo to $target"); 255c39ae2c9SAndreas Gohr $ret = 0; 256c39ae2c9SAndreas Gohr system("git clone $repo $target", $ret); 257c39ae2c9SAndreas Gohr if($ret === 0) { 2589fb66494SAndreas Gohr $this->success("cloning of $ext succeeded"); 259c39ae2c9SAndreas Gohr return true; 260c39ae2c9SAndreas Gohr } else { 2619fb66494SAndreas Gohr $this->error("cloning of $ext failed"); 262c39ae2c9SAndreas Gohr return false; 263c39ae2c9SAndreas Gohr } 264c39ae2c9SAndreas Gohr } 265c39ae2c9SAndreas Gohr 266c39ae2c9SAndreas Gohr /** 267c39ae2c9SAndreas Gohr * Returns all git repositories in this DokuWiki install 268c39ae2c9SAndreas Gohr * 269c39ae2c9SAndreas Gohr * Looks in root, template and plugin directories only. 270c39ae2c9SAndreas Gohr * 271c39ae2c9SAndreas Gohr * @return array 272c39ae2c9SAndreas Gohr */ 273c39ae2c9SAndreas Gohr private function findRepos() { 2749fb66494SAndreas Gohr $this->info('Looking for .git directories'); 275c39ae2c9SAndreas Gohr $data = array_merge( 276c39ae2c9SAndreas Gohr glob(DOKU_INC.'.git', GLOB_ONLYDIR), 277c39ae2c9SAndreas Gohr glob(DOKU_PLUGIN.'*/.git', GLOB_ONLYDIR), 278c39ae2c9SAndreas Gohr glob(fullpath(tpl_incdir().'../').'/*/.git', GLOB_ONLYDIR) 279c39ae2c9SAndreas Gohr ); 280c39ae2c9SAndreas Gohr 281c39ae2c9SAndreas Gohr if(!$data) { 2829fb66494SAndreas Gohr $this->error('Found no .git directories'); 283c39ae2c9SAndreas Gohr } else { 2849fb66494SAndreas Gohr $this->success('Found '.count($data).' .git directories'); 285c39ae2c9SAndreas Gohr } 286b0c7084fSAndreas Gohr $data = array_map('fullpath', array_map('dirname', $data)); 287c39ae2c9SAndreas Gohr return $data; 288c39ae2c9SAndreas Gohr } 289c39ae2c9SAndreas Gohr 290c39ae2c9SAndreas Gohr /** 291c39ae2c9SAndreas Gohr * Returns the repository for the given extension 292c39ae2c9SAndreas Gohr * 293c39ae2c9SAndreas Gohr * @param $extension 294c39ae2c9SAndreas Gohr * @return bool|string 295c39ae2c9SAndreas Gohr */ 296c39ae2c9SAndreas Gohr private function getSourceRepo($extension) { 297c39ae2c9SAndreas Gohr /** @var helper_plugin_extension_extension $ext */ 298c39ae2c9SAndreas Gohr $ext = plugin_load('helper', 'extension_extension'); 299c39ae2c9SAndreas Gohr if(!$ext) die("extension plugin not available, can't continue"); 300c39ae2c9SAndreas Gohr $ext->setExtension($extension); 301c39ae2c9SAndreas Gohr 30268cf024bSAndreas Gohr $repourl = $ext->getSourcerepoURL(); 30368cf024bSAndreas Gohr if(!$repourl) return false; 30468cf024bSAndreas Gohr 305c39ae2c9SAndreas Gohr // match github repos 30668cf024bSAndreas Gohr if(preg_match('/github\.com\/([^\/]+)\/([^\/]+)/i', $repourl, $m)) { 307c39ae2c9SAndreas Gohr $user = $m[1]; 308c39ae2c9SAndreas Gohr $repo = $m[2]; 309c39ae2c9SAndreas Gohr return 'https://github.com/'.$user.'/'.$repo.'.git'; 310c39ae2c9SAndreas Gohr } 311c39ae2c9SAndreas Gohr 312c39ae2c9SAndreas Gohr // match gitorious repos 31368cf024bSAndreas Gohr if(preg_match('/gitorious.org\/([^\/]+)\/([^\/]+)?/i', $repourl, $m)) { 314c39ae2c9SAndreas Gohr $user = $m[1]; 315c39ae2c9SAndreas Gohr $repo = $m[2]; 316c39ae2c9SAndreas Gohr if(!$repo) $repo = $user; 317c39ae2c9SAndreas Gohr 318c39ae2c9SAndreas Gohr return 'https://git.gitorious.org/'.$user.'/'.$repo.'.git'; 319c39ae2c9SAndreas Gohr } 320c39ae2c9SAndreas Gohr 321c39ae2c9SAndreas Gohr // match bitbucket repos - most people seem to use mercurial there though 32268cf024bSAndreas Gohr if(preg_match('/bitbucket\.org\/([^\/]+)\/([^\/]+)/i', $repourl, $m)) { 323c39ae2c9SAndreas Gohr $user = $m[1]; 324c39ae2c9SAndreas Gohr $repo = $m[2]; 325c39ae2c9SAndreas Gohr return 'https://bitbucket.org/'.$user.'/'.$repo.'.git'; 326c39ae2c9SAndreas Gohr } 327c39ae2c9SAndreas Gohr 328c39ae2c9SAndreas Gohr return false; 329c39ae2c9SAndreas Gohr } 330c39ae2c9SAndreas Gohr} 331c39ae2c9SAndreas Gohr 332b0b7909bSAndreas Gohr// Main 333b0b7909bSAndreas Gohr$cli = new GitToolCLI(); 334b0b7909bSAndreas Gohr$cli->run();