#!/usr/bin/env php */ class GitToolCLI extends CLI { /** * Register options and arguments on the given $options object * * @param Options $options * @return void */ protected function setup(Options $options) { $options->setHelp( "Manage git repositories for DokuWiki and its plugins and templates.\n\n" . "$> ./bin/gittool.php clone gallery template:ach\n" . "$> ./bin/gittool.php repos\n" . "$> ./bin/gittool.php origin -v" ); $options->registerArgument( 'command', 'Command to execute. See below', true ); $options->registerCommand( 'clone', 'Tries to install a known plugin or template (prefix with template:) via git. Uses the DokuWiki.org ' . 'plugin repository to find the proper git repository. Multiple extensions can be given as parameters' ); $options->registerArgument( 'extension', 'name of the extension to install, prefix with \'template:\' for templates', true, 'clone' ); $options->registerCommand( 'install', 'The same as clone, but when no git source repository can be found, the extension is installed via ' . 'download' ); $options->registerArgument( 'extension', 'name of the extension to install, prefix with \'template:\' for templates', true, 'install' ); $options->registerCommand( 'repos', 'Lists all git repositories found in this DokuWiki installation' ); $options->registerCommand( '*', 'Any unknown commands are assumed to be arguments to git and will be executed in all repositories ' . 'found within this DokuWiki installation' ); } /** * Your main program * * Arguments and options have been parsed when this is run * * @param Options $options * @return void */ protected function main(Options $options) { $command = $options->getCmd(); $args = $options->getArgs(); if (!$command) $command = array_shift($args); switch ($command) { case '': echo $options->help(); break; case 'clone': $this->cmdClone($args); break; case 'install': $this->cmdInstall($args); break; case 'repo': case 'repos': $this->cmdRepos(); break; default: $this->cmdGit($command, $args); } } /** * Tries to install the given extensions using git clone * * @param array $extensions */ public function cmdClone($extensions) { $errors = []; $succeeded = []; foreach ($extensions as $ext) { $repo = $this->getSourceRepo($ext); if (!$repo) { $this->error("could not find a repository for $ext"); $errors[] = $ext; } elseif ($this->cloneExtension($ext, $repo)) { $succeeded[] = $ext; } else { $errors[] = $ext; } } echo "\n"; if ($succeeded) $this->success('successfully cloned the following extensions: ' . implode(', ', $succeeded)); if ($errors) $this->error('failed to clone the following extensions: ' . implode(', ', $errors)); } /** * Tries to install the given extensions using git clone with fallback to install * * @param array $extensions */ public function cmdInstall($extensions) { $errors = []; $succeeded = []; foreach ($extensions as $ext) { $repo = $this->getSourceRepo($ext); if (!$repo) { $this->info("could not find a repository for $ext"); if ($this->downloadExtension($ext)) { $succeeded[] = $ext; } else { $errors[] = $ext; } } elseif ($this->cloneExtension($ext, $repo)) { $succeeded[] = $ext; } else { $errors[] = $ext; } } echo "\n"; if ($succeeded) $this->success('successfully installed the following extensions: ' . implode(', ', $succeeded)); if ($errors) $this->error('failed to install the following extensions: ' . implode(', ', $errors)); } /** * Executes the given git command in every repository * * @param $cmd * @param $arg */ public function cmdGit($cmd, $arg) { $repos = $this->findRepos(); $shell = array_merge(['git', $cmd], $arg); $shell = array_map('escapeshellarg', $shell); $shell = implode(' ', $shell); foreach ($repos as $repo) { if (!@chdir($repo)) { $this->error("Could not change into $repo"); continue; } $this->info("executing $shell in $repo"); $ret = 0; system($shell, $ret); if ($ret == 0) { $this->success("git succeeded in $repo"); } else { $this->error("git failed in $repo"); } } } /** * Simply lists the repositories */ public function cmdRepos() { $repos = $this->findRepos(); foreach ($repos as $repo) { echo "$repo\n"; } } /** * Install extension from the given download URL * * @param string $ext * @return bool|null */ private function downloadExtension($ext) { /** @var helper_plugin_extension_extension $plugin */ $plugin = plugin_load('helper', 'extension_extension'); if (!$ext) die("extension plugin not available, can't continue"); $plugin->setExtension($ext); $url = $plugin->getDownloadURL(); if (!$url) { $this->error("no download URL for $ext"); return false; } $ok = false; try { $this->info("installing $ext via download from $url"); $ok = $plugin->installFromURL($url); } catch (Exception $e) { $this->error($e->getMessage()); } if ($ok) { $this->success("installed $ext via download"); return true; } else { $this->success("failed to install $ext via download"); return false; } } /** * Clones the extension from the given repository * * @param string $ext * @param string $repo * @return bool */ private function cloneExtension($ext, $repo) { if (str_starts_with($ext, 'template:')) { $target = fullpath(tpl_incdir() . '../' . substr($ext, 9)); } else { $target = DOKU_PLUGIN . $ext; } $this->info("cloning $ext from $repo to $target"); $ret = 0; system("git clone $repo $target", $ret); if ($ret === 0) { $this->success("cloning of $ext succeeded"); return true; } else { $this->error("cloning of $ext failed"); return false; } } /** * Returns all git repositories in this DokuWiki install * * Looks in root, template and plugin directories only. * * @return array */ private function findRepos() { $this->info('Looking for .git directories'); $data = array_merge( glob(DOKU_INC . '.git', GLOB_ONLYDIR), glob(DOKU_PLUGIN . '*/.git', GLOB_ONLYDIR), glob(fullpath(tpl_incdir() . '../') . '/*/.git', GLOB_ONLYDIR) ); if (!$data) { $this->error('Found no .git directories'); } else { $this->success('Found ' . count($data) . ' .git directories'); } $data = array_map('fullpath', array_map('dirname', $data)); return $data; } /** * Returns the repository for the given extension * * @param $extension * @return false|string */ private function getSourceRepo($extension) { /** @var helper_plugin_extension_extension $ext */ $ext = plugin_load('helper', 'extension_extension'); if (!$ext) die("extension plugin not available, can't continue"); $ext->setExtension($extension); $repourl = $ext->getSourcerepoURL(); if (!$repourl) return false; // match github repos if (preg_match('/github\.com\/([^\/]+)\/([^\/]+)/i', $repourl, $m)) { $user = $m[1]; $repo = $m[2]; return 'https://github.com/' . $user . '/' . $repo . '.git'; } // match gitorious repos if (preg_match('/gitorious.org\/([^\/]+)\/([^\/]+)?/i', $repourl, $m)) { $user = $m[1]; $repo = $m[2]; if (!$repo) $repo = $user; return 'https://git.gitorious.org/' . $user . '/' . $repo . '.git'; } // match bitbucket repos - most people seem to use mercurial there though if (preg_match('/bitbucket\.org\/([^\/]+)\/([^\/]+)/i', $repourl, $m)) { $user = $m[1]; $repo = $m[2]; return 'https://bitbucket.org/' . $user . '/' . $repo . '.git'; } return false; } } // Main $cli = new GitToolCLI(); $cli->run();