336 lines
9.8 KiB
PHP
336 lines
9.8 KiB
PHP
|
#!/usr/bin/php
|
||
|
<?php
|
||
|
if(!defined('DOKU_INC')) define('DOKU_INC', realpath(dirname(__FILE__).'/../').'/');
|
||
|
define('NOSESSION', 1);
|
||
|
require_once(DOKU_INC.'inc/init.php');
|
||
|
|
||
|
/**
|
||
|
* Easily manage DokuWiki git repositories
|
||
|
*
|
||
|
* @author Andreas Gohr <andi@splitbrain.org>
|
||
|
*/
|
||
|
class GitToolCLI extends DokuCLI {
|
||
|
|
||
|
/**
|
||
|
* Register options and arguments on the given $options object
|
||
|
*
|
||
|
* @param DokuCLI_Options $options
|
||
|
* @return void
|
||
|
*/
|
||
|
protected function setup(DokuCLI_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 DokuCLI_Options $options
|
||
|
* @return void
|
||
|
*/
|
||
|
protected function main(DokuCLI_Options $options) {
|
||
|
$command = $options->getCmd();
|
||
|
if(!$command) $command = array_shift($options->args);
|
||
|
|
||
|
switch($command) {
|
||
|
case '':
|
||
|
echo $options->help();
|
||
|
break;
|
||
|
case 'clone':
|
||
|
$this->cmd_clone($options->args);
|
||
|
break;
|
||
|
case 'install':
|
||
|
$this->cmd_install($options->args);
|
||
|
break;
|
||
|
case 'repo':
|
||
|
case 'repos':
|
||
|
$this->cmd_repos();
|
||
|
break;
|
||
|
default:
|
||
|
$this->cmd_git($command, $options->args);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Tries to install the given extensions using git clone
|
||
|
*
|
||
|
* @param array $extensions
|
||
|
*/
|
||
|
public function cmd_clone($extensions) {
|
||
|
$errors = array();
|
||
|
$succeeded = array();
|
||
|
|
||
|
foreach($extensions as $ext) {
|
||
|
$repo = $this->getSourceRepo($ext);
|
||
|
|
||
|
if(!$repo) {
|
||
|
$this->error("could not find a repository for $ext");
|
||
|
$errors[] = $ext;
|
||
|
} else {
|
||
|
if($this->cloneExtension($ext, $repo)) {
|
||
|
$succeeded[] = $ext;
|
||
|
} else {
|
||
|
$errors[] = $ext;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
echo "\n";
|
||
|
if($succeeded) $this->success('successfully cloned the following extensions: '.join(', ', $succeeded));
|
||
|
if($errors) $this->error('failed to clone the following extensions: '.join(', ', $errors));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Tries to install the given extensions using git clone with fallback to install
|
||
|
*
|
||
|
* @param array $extensions
|
||
|
*/
|
||
|
public function cmd_install($extensions) {
|
||
|
$errors = array();
|
||
|
$succeeded = array();
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
} else {
|
||
|
if($this->cloneExtension($ext, $repo)) {
|
||
|
$succeeded[] = $ext;
|
||
|
} else {
|
||
|
$errors[] = $ext;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
echo "\n";
|
||
|
if($succeeded) $this->success('successfully installed the following extensions: '.join(', ', $succeeded));
|
||
|
if($errors) $this->error('failed to install the following extensions: '.join(', ', $errors));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Executes the given git command in every repository
|
||
|
*
|
||
|
* @param $cmd
|
||
|
* @param $arg
|
||
|
*/
|
||
|
public function cmd_git($cmd, $arg) {
|
||
|
$repos = $this->findRepos();
|
||
|
|
||
|
$shell = array_merge(array('git', $cmd), $arg);
|
||
|
$shell = array_map('escapeshellarg', $shell);
|
||
|
$shell = join(' ', $shell);
|
||
|
|
||
|
foreach($repos as $repo) {
|
||
|
if(!@chdir($repo)) {
|
||
|
$this->error("Could not change into $repo");
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
echo "\n";
|
||
|
$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 cmd_repos() {
|
||
|
$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(substr($ext, 0, 9) == '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();
|