From 8aae0f29d4d37135382c18f0fb8c08059ce1a14f Mon Sep 17 00:00:00 2001 From: Thomas Keller Date: Sat, 28 Aug 2010 14:06:17 +0000 Subject: [PATCH 01/15] refactor the basicio parser into a separate utility class (we soon need a compiler part as well) --- src/IDF/Scm/Monotone.php | 82 ++--------------------- src/IDF/Scm/Monotone/BasicIO.php | 111 +++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+), 75 deletions(-) create mode 100644 src/IDF/Scm/Monotone/BasicIO.php diff --git a/src/IDF/Scm/Monotone.php b/src/IDF/Scm/Monotone.php index dd3679f..d414730 100644 --- a/src/IDF/Scm/Monotone.php +++ b/src/IDF/Scm/Monotone.php @@ -22,6 +22,7 @@ # ***** END LICENSE BLOCK ***** */ require_once(dirname(__FILE__) . "/Monotone/Stdio.php"); +require_once(dirname(__FILE__) . "/Monotone/BasicIO.php"); /** * Monotone scm class @@ -143,75 +144,6 @@ class IDF_Scm_Monotone extends IDF_Scm return preg_split("/\n/", $out, -1, PREG_SPLIT_NO_EMPTY); } - /** - * Parses monotone's basic_io format - * - * @param string $in - * @return array of arrays - */ - private static function _parseBasicIO($in) - { - $pos = 0; - $stanzas = array(); - - while ($pos < strlen($in)) { - $stanza = array(); - while ($pos < strlen($in)) { - if ($in[$pos] == "\n") break; - - $stanzaLine = array('key' => '', 'values' => array(), 'hash' => null); - while ($pos < strlen($in)) { - $ch = $in[$pos]; - if ($ch == '"' || $ch == '[') break; - ++$pos; - if ($ch == ' ') continue; - $stanzaLine['key'] .= $ch; - } - - if ($in[$pos] == '[') { - ++$pos; // opening square bracket - $stanzaLine['hash'] = substr($in, $pos, 40); - $pos += 40; - ++$pos; // closing square bracket - } - else - { - $valCount = 0; - while ($in[$pos] == '"') { - ++$pos; // opening quote - $stanzaLine['values'][$valCount] = ''; - while ($pos < strlen($in)) { - $ch = $in[$pos]; $pr = $in[$pos-1]; - if ($ch == '"' && $pr != '\\') break; - ++$pos; - $stanzaLine['values'][$valCount] .= $ch; - } - ++$pos; // closing quote - - if ($in[$pos] == ' ') { - ++$pos; // space - ++$valCount; - } - } - - for ($i = 0; $i <= $valCount; $i++) { - $stanzaLine['values'][$i] = str_replace( - array("\\\\", "\\\""), - array("\\", "\""), - $stanzaLine['values'][$i] - ); - } - } - - $stanza[] = $stanzaLine; - ++$pos; // newline - } - $stanzas[] = $stanza; - ++$pos; // newline - } - return $stanzas; - } - /** * Queries the certs for a given revision and returns them in an * associative array array("branch" => array("branch1", ...), ...) @@ -226,7 +158,7 @@ class IDF_Scm_Monotone extends IDF_Scm if (!array_key_exists($rev, $certCache)) { $out = $this->stdio->exec(array('certs', $rev)); - $stanzas = self::_parseBasicIO($out); + $stanzas = IDF_Scm_Monotone_BasicIO::parse($out); $certs = array(); foreach ($stanzas as $stanza) { $certname = null; @@ -290,7 +222,7 @@ class IDF_Scm_Monotone extends IDF_Scm 'get_content_changed', $startrev, $file )); - $stanzas = self::_parseBasicIO($out); + $stanzas = IDF_Scm_Monotone_BasicIO::parse($out); // FIXME: we only care about the first returned content mark // everything else seem to be very, very rare cases @@ -326,7 +258,7 @@ class IDF_Scm_Monotone extends IDF_Scm $out = $this->stdio->exec(array('tags')); $tags = array(); - $stanzas = self::_parseBasicIO($out); + $stanzas = IDF_Scm_Monotone_BasicIO::parse($out); foreach ($stanzas as $stanza) { $tagname = null; foreach ($stanza as $stanzaline) { @@ -375,7 +307,7 @@ class IDF_Scm_Monotone extends IDF_Scm )); $files = array(); - $stanzas = self::_parseBasicIO($out); + $stanzas = IDF_Scm_Monotone_BasicIO::parse($out); $folder = $folder == '/' || empty($folder) ? '' : $folder.'/'; foreach ($stanzas as $stanza) { @@ -525,7 +457,7 @@ class IDF_Scm_Monotone extends IDF_Scm )); $files = array(); - $stanzas = self::_parseBasicIO($out); + $stanzas = IDF_Scm_Monotone_BasicIO::parse($out); foreach ($stanzas as $stanza) { if ($stanza[0]['key'] == 'format_version') @@ -666,7 +598,7 @@ class IDF_Scm_Monotone extends IDF_Scm )); $newAndPatchedFiles = 0; - $stanzas = self::_parseBasicIO($out); + $stanzas = IDF_Scm_Monotone_BasicIO::parse($out); foreach ($stanzas as $stanza) { if ($stanza[0]['key'] == 'patch' || $stanza[0]['key'] == 'add_file') diff --git a/src/IDF/Scm/Monotone/BasicIO.php b/src/IDF/Scm/Monotone/BasicIO.php new file mode 100644 index 0000000..a0a5fd7 --- /dev/null +++ b/src/IDF/Scm/Monotone/BasicIO.php @@ -0,0 +1,111 @@ + + */ +class IDF_Scm_Monotone_BasicIO extends IDF_Scm +{ + /** + * Parses monotone's basic_io format + * + * @param string $in + * @return array of arrays + */ + public static function parse($in) + { + $pos = 0; + $stanzas = array(); + + while ($pos < strlen($in)) { + $stanza = array(); + while ($pos < strlen($in)) { + if ($in[$pos] == "\n") break; + + $stanzaLine = array('key' => '', 'values' => array(), 'hash' => null); + while ($pos < strlen($in)) { + $ch = $in[$pos]; + if ($ch == '"' || $ch == '[') break; + ++$pos; + if ($ch == ' ') continue; + $stanzaLine['key'] .= $ch; + } + + if ($in[$pos] == '[') { + ++$pos; // opening square bracket + $stanzaLine['hash'] = substr($in, $pos, 40); + $pos += 40; + ++$pos; // closing square bracket + } + else + { + $valCount = 0; + while ($in[$pos] == '"') { + ++$pos; // opening quote + $stanzaLine['values'][$valCount] = ''; + while ($pos < strlen($in)) { + $ch = $in[$pos]; $pr = $in[$pos-1]; + if ($ch == '"' && $pr != '\\') break; + ++$pos; + $stanzaLine['values'][$valCount] .= $ch; + } + ++$pos; // closing quote + + if ($in[$pos] == ' ') { + ++$pos; // space + ++$valCount; + } + } + + for ($i = 0; $i <= $valCount; $i++) { + $stanzaLine['values'][$i] = str_replace( + array("\\\\", "\\\""), + array("\\", "\""), + $stanzaLine['values'][$i] + ); + } + } + + $stanza[] = $stanzaLine; + ++$pos; // newline + } + $stanzas[] = $stanza; + ++$pos; // newline + } + return $stanzas; + } + + /** + * Compiles monotone's basicio format + * + * @param array $in Array of arrays + * @return string + */ + public static function compile($in) + { + throw new IDF_Scm_Exception("not yet implemented"); + } +} + From 593240b4207742099e20d84826c0ae9533f547b0 Mon Sep 17 00:00:00 2001 From: Thomas Keller Date: Sat, 28 Aug 2010 22:03:47 +0000 Subject: [PATCH 02/15] implement IDF_Scm_Monotone_BasicIO::compile() --- src/IDF/Scm/Monotone/BasicIO.php | 55 ++++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/src/IDF/Scm/Monotone/BasicIO.php b/src/IDF/Scm/Monotone/BasicIO.php index a0a5fd7..0562c9b 100644 --- a/src/IDF/Scm/Monotone/BasicIO.php +++ b/src/IDF/Scm/Monotone/BasicIO.php @@ -26,7 +26,7 @@ * * @author Thomas Keller */ -class IDF_Scm_Monotone_BasicIO extends IDF_Scm +class IDF_Scm_Monotone_BasicIO { /** * Parses monotone's basic_io format @@ -54,6 +54,7 @@ class IDF_Scm_Monotone_BasicIO extends IDF_Scm } if ($in[$pos] == '[') { + unset($stanzaLine['values']); ++$pos; // opening square bracket $stanzaLine['hash'] = substr($in, $pos, 40); $pos += 40; @@ -61,6 +62,7 @@ class IDF_Scm_Monotone_BasicIO extends IDF_Scm } else { + unset($stanzaLine['hash']); $valCount = 0; while ($in[$pos] == '"') { ++$pos; // opening quote @@ -105,7 +107,56 @@ class IDF_Scm_Monotone_BasicIO extends IDF_Scm */ public static function compile($in) { - throw new IDF_Scm_Exception("not yet implemented"); + $out = ""; + $first = true; + foreach ((array)$in as $sx => $stanza) { + if ($first) + $first = false; + else + $out .= "\n"; + + $maxkeylength = 0; + foreach ((array)$stanza as $lx => $line) { + if (!array_key_exists('key', $line)) { + throw new IDF_Scm_Exception( + '"key" not found in basicio stanza '.$sx.', line '.$lx + ); + } + $maxkeylength = max($maxkeylength, strlen($line['key'])); + } + + foreach ((array)$stanza as $lx => $line) { + $out .= str_pad($line['key'], $maxkeylength, ' ', STR_PAD_LEFT); + + if (array_key_exists('hash', $line)) { + $out .= ' ['.$line['hash'].']'; + } else + if (array_key_exists('values', $line)) { + if (!is_array($line['values']) || count($line['values']) == 0) { + throw new IDF_Scm_Exception( + '"values" must be an array of a size >= 1 '. + 'in basicio stanza '.$sx.', line '.$lx + ); + } + foreach ($line['values'] as $value) { + $out .= ' "'.str_replace( + array("\\", "\""), + array("\\\\", "\\\""), + $value).'"'; + } + } + else + { + throw new IDF_Scm_Exception( + 'neither "hash" nor "values" found in basicio '. + 'stanza '.$sx.', line '.$lx + ); + } + + $out .= "\n"; + } + } + return $out; } } From f2a9518b5cea049007701300f723fd874b1aca7a Mon Sep 17 00:00:00 2001 From: Thomas Keller Date: Sat, 28 Aug 2010 22:47:37 +0000 Subject: [PATCH 03/15] single quoted newlines of course will not work here... stupid me --- src/IDF/Scm/Monotone/Usher.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/IDF/Scm/Monotone/Usher.php b/src/IDF/Scm/Monotone/Usher.php index 9891555..5be2e8e 100644 --- a/src/IDF/Scm/Monotone/Usher.php +++ b/src/IDF/Scm/Monotone/Usher.php @@ -215,7 +215,7 @@ class IDF_Scm_Monotone_Usher ); } - fwrite($sock, 'USERPASS '.$uc['user'].' '.$uc['pass'].'\n'); + fwrite($sock, 'USERPASS '.$uc['user'].' '.$uc['pass']."\n"); if (feof($sock)) { throw new IDF_Scm_Exception( 'usher closed the connection - probably wrong admin '. @@ -223,7 +223,7 @@ class IDF_Scm_Monotone_Usher ); } - fwrite($sock, $cmd.'\n'); + fwrite($sock, $cmd."\n"); $out = ''; while (!feof($sock)) { $out .= fgets($sock); From af3df142d409a6d51c1ab9a339952ace67c135f3 Mon Sep 17 00:00:00 2001 From: Thomas Keller Date: Sat, 28 Aug 2010 23:10:08 +0000 Subject: [PATCH 04/15] First attempt on a monotone plugin which creates a new database upon project creation and adds the new server to the running usher instance. If everything goes well, the usher instance is told to reload its configuration, so the new server / database is picked up and started automatically. --- src/IDF/Plugin/SyncMonotone.php | 137 ++++++++++++++++++++++++++++++++ src/IDF/conf/idf.php-dist | 13 +-- src/IDF/relations.php | 42 +++++----- 3 files changed, 168 insertions(+), 24 deletions(-) create mode 100644 src/IDF/Plugin/SyncMonotone.php diff --git a/src/IDF/Plugin/SyncMonotone.php b/src/IDF/Plugin/SyncMonotone.php new file mode 100644 index 0000000..2280a0b --- /dev/null +++ b/src/IDF/Plugin/SyncMonotone.php @@ -0,0 +1,137 @@ +processMonotoneCreate($params['project']); + break; + } + } + + /** + * Run mtn init command to create the corresponding monotone + * repository and add the database to the configured usher instance + * + * @param IDF_Project + */ + function processMonotoneCreate($project) + { + if ($project->getConf()->getVal('scm') != 'mtn') { + return; + } + + $repotempl = Pluf::f('mtn_repositories', false); + if ($repotempl === false) { + throw new IDF_Scm_Exception( + '"mtn_repositories" must be defined in your configuration file.' + ); + } + + $usher_config = Pluf::f('mtn_usher', array()); + if (!array_key_exists('rcfile', $usher_config) || + !is_writable($usher_config['rcfile'])) { + throw new IDF_Scm_Exception( + '"rcfile" in "mtn_usher" does not exist or is not writable.' + ); + } + + $shortname = $project->shortname; + $dbfile = sprintf($repotempl, $shortname); + if (file_exists($dbfile)) { + throw new IDF_Scm_Exception(sprintf( + __('The repository %s already exists.'), $dbfile + )); + } + $return = 0; + $output = array(); + $cmd = sprintf( + Pluf::f('mtn_path', 'mtn').' db init -d %s', + escapeshellarg($dbfile) + ); + $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd; + $ll = exec($cmd, $output, $return); + if ($return != 0) { + throw new IDF_Scm_Exception(sprintf( + __('Could not create repository %s - please check '. + 'your error log for details.'), + $dbfile + )); + } + + $usher_rc = file_get_contents($usher_config['rcfile']); + $parsed_config = array(); + try { + $parsed_config = IDF_Scm_Monotone_BasicIO::parse($usher_rc); + } + catch (Exception $e) { + throw new IDF_Scm_Exception(sprintf( + __('Could not parse usher configuration in "%s": %s'), + $usher_config['rcfile'], $e->getMessage() + )); + } + + // ensure we haven't configured a server with this name already + foreach ($parsed_config as $stanzas) + { + foreach ($stanzas as $stanza_line) + { + if ($stanza_line['key'] == 'server' && + $stanza_line['values'][0] == $shortname) + { + throw new IDF_Scm_Exception(sprintf( + __('usher configuration already contains a server '. + 'entry named "%s"'), + $shortname + )); + } + } + } + + $new_server = array( + array('key' => 'server', 'values' => array($shortname)), + array('key' => 'local', 'values' => array('-d', $dbfile)), + ); + + $parsed_config[] = $new_server; + $usher_rc = IDF_Scm_Monotone_BasicIO::compile($parsed_config); + + // FIXME: more sanity - what happens on failing writes? + $fp = fopen($usher_config['rcfile'], 'w'); + fwrite($fp, $usher_rc); + fclose($fp); + + IDF_Scm_Monotone_Usher::reload(); + } +} diff --git a/src/IDF/conf/idf.php-dist b/src/IDF/conf/idf.php-dist index 390ce36..31c4661 100644 --- a/src/IDF/conf/idf.php-dist +++ b/src/IDF/conf/idf.php-dist @@ -147,13 +147,16 @@ $cfg['mtn_db_access'] = 'remote'; # # 'host' and 'port' must be set to the specific bits from usher's # configured 'adminaddr', 'user' and 'pass' must match the values set for -# the configured 'userpass' combination +# the configured 'userpass' combination. The 'rcfile' variable must point +# to the full (writable) path of the usher configuration file which gets +# updated when new projects are added # #$cfg['mtn_usher'] = array( -# 'host' => 'localhost', -# 'port' => 12345, -# 'user' => 'admin', -# 'pass' => 'admin', +# 'host' => 'localhost', +# 'port' => 12345, +# 'user' => 'admin', +# 'pass' => 'admin', +# 'rcfile' => '/path/to/usher.conf', #); # diff --git a/src/IDF/relations.php b/src/IDF/relations.php index f5a5925..bf4e32e 100644 --- a/src/IDF/relations.php +++ b/src/IDF/relations.php @@ -49,51 +49,55 @@ Pluf_Signal::connect('Pluf_Template_Compiler::construct_template_tags_modifiers' # -- Standard plugins, they will run only if configured -- # # Subversion synchronization -Pluf_Signal::connect('IDF_Project::membershipsUpdated', +Pluf_Signal::connect('IDF_Project::membershipsUpdated', array('IDF_Plugin_SyncSvn', 'entry')); -Pluf_Signal::connect('IDF_Project::created', +Pluf_Signal::connect('IDF_Project::created', array('IDF_Plugin_SyncSvn', 'entry')); -Pluf_Signal::connect('Pluf_User::passwordUpdated', +Pluf_Signal::connect('Pluf_User::passwordUpdated', array('IDF_Plugin_SyncSvn', 'entry')); -Pluf_Signal::connect('IDF_Project::preDelete', +Pluf_Signal::connect('IDF_Project::preDelete', array('IDF_Plugin_SyncSvn', 'entry')); -Pluf_Signal::connect('svnpostcommit.php::run', +Pluf_Signal::connect('svnpostcommit.php::run', array('IDF_Plugin_SyncSvn', 'entry')); # # Mercurial synchronization -Pluf_Signal::connect('IDF_Project::membershipsUpdated', +Pluf_Signal::connect('IDF_Project::membershipsUpdated', array('IDF_Plugin_SyncMercurial', 'entry')); -Pluf_Signal::connect('IDF_Project::created', +Pluf_Signal::connect('IDF_Project::created', array('IDF_Plugin_SyncMercurial', 'entry')); -Pluf_Signal::connect('Pluf_User::passwordUpdated', +Pluf_Signal::connect('Pluf_User::passwordUpdated', array('IDF_Plugin_SyncMercurial', 'entry')); -Pluf_Signal::connect('hgchangegroup.php::run', +Pluf_Signal::connect('hgchangegroup.php::run', array('IDF_Plugin_SyncMercurial', 'entry')); # # Git synchronization -Pluf_Signal::connect('IDF_Project::membershipsUpdated', +Pluf_Signal::connect('IDF_Project::membershipsUpdated', array('IDF_Plugin_SyncGit', 'entry')); -Pluf_Signal::connect('IDF_Key::postSave', +Pluf_Signal::connect('IDF_Key::postSave', array('IDF_Plugin_SyncGit', 'entry')); -Pluf_Signal::connect('IDF_Project::created', +Pluf_Signal::connect('IDF_Project::created', array('IDF_Plugin_SyncGit', 'entry')); -Pluf_Signal::connect('IDF_Key::preDelete', +Pluf_Signal::connect('IDF_Key::preDelete', array('IDF_Plugin_SyncGit', 'entry')); -Pluf_Signal::connect('gitpostupdate.php::run', +Pluf_Signal::connect('gitpostupdate.php::run', array('IDF_Plugin_SyncGit', 'entry')); # +# monotone synchronization +Pluf_Signal::connect('IDF_Project::created', + array('IDF_Plugin_SyncMonotone', 'entry')); +# # -- Processing of the webhook queue -- -Pluf_Signal::connect('queuecron.php::run', +Pluf_Signal::connect('queuecron.php::run', array('IDF_Queue', 'process')); # -# Processing of a given webhook, the hook can be configured -# directly in the configuration file if a different solution +# Processing of a given webhook, the hook can be configured +# directly in the configuration file if a different solution # is required. -Pluf_Signal::connect('IDF_Queue::processItem', - Pluf::f('idf_hook_process_item', +Pluf_Signal::connect('IDF_Queue::processItem', + Pluf::f('idf_hook_process_item', array('IDF_Webhook', 'process'))); return $m; From 194dcad0e3e76372b3d42348d8aed58f6b09c1cd Mon Sep 17 00:00:00 2001 From: Thomas Keller Date: Sun, 29 Aug 2010 22:59:12 +0000 Subject: [PATCH 05/15] Don't check if the main branch is empty, but let the view code later handle the case where a selector doesn't resolve to at least one revision gracefully (see also issue 525). --- src/IDF/Scm/Monotone.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/IDF/Scm/Monotone.php b/src/IDF/Scm/Monotone.php index d414730..f8c898f 100644 --- a/src/IDF/Scm/Monotone.php +++ b/src/IDF/Scm/Monotone.php @@ -122,12 +122,6 @@ class IDF_Scm_Monotone extends IDF_Scm $branch = "*"; } - if (count($this->_resolveSelector("h:$branch")) == 0) { - throw new IDF_Scm_Exception( - "Branch $branch is empty" - ); - } - return $branch; } From b648e6f7a72f84d49db67a03c7b6822105876253 Mon Sep 17 00:00:00 2001 From: Thomas Keller Date: Sun, 29 Aug 2010 23:01:25 +0000 Subject: [PATCH 06/15] - now that we have to configure usher's configuration file anyway, we can skip the explicit configuration of its host and admin password, as we can directly read that from the configuration file itself - expand the SyncMonotone plugin a bit to where this actually becomes useful, i.e. create an accompanying key for each created database and also add some initial database-specific configuration - update the config docs in idf.php-dist to reflect the changes and add more details about the inner workings of the SyncMonotone plugin --- src/IDF/Middleware.php | 2 +- src/IDF/Plugin/SyncMonotone.php | 123 ++++++++++++++++++++++++++------ src/IDF/Scm/Monotone/Usher.php | 44 +++++++++--- src/IDF/conf/idf.php-dist | 62 ++++++++-------- src/IDF/conf/urls.php | 2 +- 5 files changed, 170 insertions(+), 63 deletions(-) diff --git a/src/IDF/Middleware.php b/src/IDF/Middleware.php index cee2270..424cc01 100644 --- a/src/IDF/Middleware.php +++ b/src/IDF/Middleware.php @@ -111,7 +111,7 @@ function IDF_Middleware_ContextPreProcessor($request) $request->project); $c = array_merge($c, $request->rights); } - $c['usherConfigured'] = Pluf::f("mtn_usher", null) !== null; + $c['usherConfigured'] = Pluf::f("mtn_usher_conf", null) !== null; return $c; } diff --git a/src/IDF/Plugin/SyncMonotone.php b/src/IDF/Plugin/SyncMonotone.php index 2280a0b..6f37fb8 100644 --- a/src/IDF/Plugin/SyncMonotone.php +++ b/src/IDF/Plugin/SyncMonotone.php @@ -41,8 +41,15 @@ class IDF_Plugin_SyncMonotone } /** - * Run mtn init command to create the corresponding monotone - * repository and add the database to the configured usher instance + * Four steps to setup a new monotone project: + * + * 1) run mtn db init to initialize a new database underknees + * 'mtn_repositories' + * 2) create a new server key in the same directory + * 3) write monotonerc for access control + * 4) add the database as new local server in the usher configuration + * 5) reload the running usher instance so it acknowledges the new + * server * * @param IDF_Project */ @@ -52,45 +59,113 @@ class IDF_Plugin_SyncMonotone return; } - $repotempl = Pluf::f('mtn_repositories', false); - if ($repotempl === false) { + $projecttempl = Pluf::f('mtn_repositories', false); + if ($projecttempl === false) { throw new IDF_Scm_Exception( '"mtn_repositories" must be defined in your configuration file.' ); } - $usher_config = Pluf::f('mtn_usher', array()); - if (!array_key_exists('rcfile', $usher_config) || - !is_writable($usher_config['rcfile'])) { + $usher_config = Pluf::f('mtn_usher_conf', false); + if (!$usher_config || !is_writable($usher_config)) { throw new IDF_Scm_Exception( - '"rcfile" in "mtn_usher" does not exist or is not writable.' + '"mtn_usher_conf" does not exist or is not writable.' ); } $shortname = $project->shortname; - $dbfile = sprintf($repotempl, $shortname); - if (file_exists($dbfile)) { + $projectpath = sprintf($projecttempl, $shortname); + if (file_exists($projectpath)) { throw new IDF_Scm_Exception(sprintf( - __('The repository %s already exists.'), $dbfile + __('The project path %s already exists.'), $projectpath )); } - $return = 0; - $output = array(); + + if (!mkdir($projectpath)) { + throw new IDF_Scm_Exception(sprintf( + __('The project path %s could not be created.'), $projectpath + )); + } + + // + // step 1) create a new database + // + $dbfile = $projectpath.'/database.mtn'; $cmd = sprintf( Pluf::f('mtn_path', 'mtn').' db init -d %s', escapeshellarg($dbfile) ); $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd; - $ll = exec($cmd, $output, $return); + $ll = exec($cmd, $output = array(), $return = 0); if ($return != 0) { throw new IDF_Scm_Exception(sprintf( - __('Could not create repository %s - please check '. - 'your error log for details.'), - $dbfile + __('The database file %s could not be created.'), $dbfile )); } - $usher_rc = file_get_contents($usher_config['rcfile']); + // + // step 2) create a server key + // + // try to parse the key's domain part from the remote_url's host + // name, otherwise fall back to the configured Apache server name + $server = $_SERVER['SERVER_NAME']; + $remote_url = Pluf::f('mtn_remote_url'); + if (($parsed = parse_url($remote_url)) !== false && + !empty($parsed['host'])) { + $server = $parsed['host']; + } + + $keyname = $shortname.'-server@'.$server; + $cmd = sprintf( + Pluf::f('mtn_path', 'mtn').' au genkey --confdir=%s %s ""', + escapeshellarg($projectpath), + escapeshellarg($keyname) + ); + $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd; + $ll = exec($cmd, $output = array(), $return = 0); + if ($return != 0) { + throw new IDF_Scm_Exception(sprintf( + __('The server key %s could not be created.'), $keyname + )); + } + + // + // step 3) write monotonerc for access control + // FIXME: netsync access control is still missing! + // + $monotonerc =<<getMessage() + $usher_config, $e->getMessage() )); } @@ -121,17 +196,23 @@ class IDF_Plugin_SyncMonotone $new_server = array( array('key' => 'server', 'values' => array($shortname)), - array('key' => 'local', 'values' => array('-d', $dbfile)), + array('key' => 'local', 'values' => array( + '--confdir', $projectpath, + '-d', $dbfile + )), ); $parsed_config[] = $new_server; $usher_rc = IDF_Scm_Monotone_BasicIO::compile($parsed_config); // FIXME: more sanity - what happens on failing writes? - $fp = fopen($usher_config['rcfile'], 'w'); + $fp = fopen($usher_config, 'w'); fwrite($fp, $usher_rc); fclose($fp); + // + // step 5) reload usher to pick up the new configuration + // IDF_Scm_Monotone_Usher::reload(); } } diff --git a/src/IDF/Scm/Monotone/Usher.php b/src/IDF/Scm/Monotone/Usher.php index 5be2e8e..5e55e08 100644 --- a/src/IDF/Scm/Monotone/Usher.php +++ b/src/IDF/Scm/Monotone/Usher.php @@ -21,6 +21,8 @@ # # ***** END LICENSE BLOCK ***** */ +require_once(dirname(__FILE__) . "/BasicIO.php"); + /** * Connects with the admininistrative interface of usher, * the monotone proxy. This class contains only static methods because @@ -190,36 +192,56 @@ class IDF_Scm_Monotone_Usher private static function _triggerCommand($cmd) { - $uc = Pluf::f('mtn_usher'); - if (empty($uc['host'])) { + $uc = Pluf::f('mtn_usher_conf', false); + if (!$uc || !is_readable($uc)) { + throw new IDF_Scm_Exception( + '"mtn_usher_conf" is not configured or not readable' + ); + } + + $parsed_config = + IDF_Scm_Monotone_BasicIO::parse(file_get_contents($uc)); + $host = $port = $user = $pass = null; + foreach ($parsed_config as $stanza) { + foreach ($stanza as $line) { + if ($line['key'] == 'adminaddr') { + list($host, $port) = explode(":", @$line['values'][0]); + break; + } + if ($line['key'] == 'userpass') { + $user = @$line['values'][0]; + $pass = @$line['values'][1]; + } + } + } + + if (empty($host)) { throw new IDF_Scm_Exception('usher host is empty'); } - if (!preg_match('/^\d+$/', $uc['port']) || - $uc['port'] == 0) + if (!preg_match('/^\d+$/', $port)) { throw new IDF_Scm_Exception('usher port is invalid'); } - if (empty($uc['user'])) { + if (empty($user)) { throw new IDF_Scm_Exception('usher user is empty'); } - if (empty($uc['pass'])) { + if (empty($pass)) { throw new IDF_Scm_Exception('usher pass is empty'); } - $sock = @fsockopen($uc['host'], $uc['port'], $errno, $errstr); + $sock = @fsockopen($host, $port, $errno, $errstr); if (!$sock) { throw new IDF_Scm_Exception( "could not connect to usher: $errstr ($errno)" ); } - fwrite($sock, 'USERPASS '.$uc['user'].' '.$uc['pass']."\n"); + fwrite($sock, 'USERPASS '.$user.' '.$pass."\n"); if (feof($sock)) { throw new IDF_Scm_Exception( - 'usher closed the connection - probably wrong admin '. - 'username or password' + 'usher closed the connection - this should not happen' ); } @@ -232,7 +254,7 @@ class IDF_Scm_Monotone_Usher $out = rtrim($out); if ($out == 'unknown command') { - throw new IDF_Scm_Exception("unknown command: $cmd"); + throw new IDF_Scm_Exception('unknown command: '.$cmd); } return $out; diff --git a/src/IDF/conf/idf.php-dist b/src/IDF/conf/idf.php-dist index 31c4661..57d3e95 100644 --- a/src/IDF/conf/idf.php-dist +++ b/src/IDF/conf/idf.php-dist @@ -78,9 +78,11 @@ $cfg['mtn_path'] = 'mtn'; # Additional options for the started monotone process $cfg['mtn_opts'] = array('--no-workspace', '--norc'); # -# You can setup monotone for use with indefero in two ways: +# You can setup monotone for use with indefero in several ways. The +# two most-used should be: # # 1) One database for everything: +# # Set 'mtn_repositories' below to a fixed database path, such as # '/home/mtn/repositories/all_projects.mtn' # @@ -94,26 +96,39 @@ $cfg['mtn_opts'] = array('--no-workspace', '--norc'); # the database # # 2) One database for every project with 'usher': -# Set 'mtn_remote_url' below to a string which matches your setup. +# +# Download and configure 'usher' +# (mtn clone mtn://monotone.ca?net.venge.monotone.contrib.usher) +# which acts as proxy in front of all single project databases. +# Create a basic configuration file for it and add a secret admin +# username and password. Finally, point the below variable +# 'mtn_usher_conf' to this configuration file. +# +# Then set 'mtn_remote_url' below to a string which matches your setup. # Again, the '%s' placeholder will be expanded to the project's # short name. Note that 'mtn_remote_url' is used as internal # URI (to access the data for indefero) as well as external URI -# (for end users) at the same time. +# (for end users) at the same time. 'mtn_repositories' should then +# point to a directory where all project-related files (databases, +# keys, configurations) are kept, as these are automatically created +# on project creation by IDF. # -# Then download and configure 'usher' -# (mtn clone mtn://monotone.ca?net.venge.monotone.contrib.usher) -# which acts as proxy in front of all single project databases. -# Usher's server names should be mapped to the project's short names, -# so you end up with something like this for every project: +# Example: 'mtn_repositories' is configured to be '/var/monotone/%s' # -# server "project" -# local "-d" "/home/mtn/repositories/project.mtn" "*" +# - IDF tries to create /var/monotone/ as root directory +# - The database is placed in as /var/monotone//database.mtn +# - The server key is put into /var/monotone//keys and +# is named "-server@", where host is the host part +# of 'mtn_remote_url' # -# Alternatively if you assign every project a unique DNS such as -# 'project.my-hosting.biz', you can also configure it like this: +# therefor /var/monotone MUST be read/writable for the www user and all +# files which are created underknees MUST be read/writable by the user +# who is executing the usher instance! The best way to achieve this is with +# default (POSIX) ACLs on /var/monotone. # -# host "project.my-hosting.biz" -# local "-d" "/home/mtn/repositories/project.mtn" "*" +# +# You could also choose to setup usher by hand, i.e. with individual +# databases, in this case leave 'mtn_usher_conf' below commented out. # # Pro: - read and write access can be granted per project # - no database locking issues @@ -143,22 +158,11 @@ $cfg['mtn_remote_url'] = 'mtn://my-host.biz/%s'; $cfg['mtn_db_access'] = 'remote'; # # If configured, this allows basic control of a running usher process -# via the forge administration -# -# 'host' and 'port' must be set to the specific bits from usher's -# configured 'adminaddr', 'user' and 'pass' must match the values set for -# the configured 'userpass' combination. The 'rcfile' variable must point -# to the full (writable) path of the usher configuration file which gets -# updated when new projects are added -# -#$cfg['mtn_usher'] = array( -# 'host' => 'localhost', -# 'port' => 12345, -# 'user' => 'admin', -# 'pass' => 'admin', -# 'rcfile' => '/path/to/usher.conf', -#); +# via the forge administration. The variable must point to the full (writable) +# path of the usher configuration file which gets updated when new projects +# are added # +#$cfg['mtn_usher_conf'] = '/path/to/usher.conf'; # Mercurial repositories path #$cfg['mercurial_repositories'] = '/home/mercurial/repositories/%s'; diff --git a/src/IDF/conf/urls.php b/src/IDF/conf/urls.php index 1d9b56f..b4f6a4c 100644 --- a/src/IDF/conf/urls.php +++ b/src/IDF/conf/urls.php @@ -386,7 +386,7 @@ $ctl[] = array('regex' => '#^/admin/users/(\d+)/$#', 'model' => 'IDF_Views_Admin', 'method' => 'userUpdate'); -if (Pluf::f("mtn_usher", null) !== null) +if (Pluf::f("mtn_usher_conf", null) !== null) { $ctl[] = array('regex' => '#^/admin/usher/$#', 'base' => $base, From adae73080c7f83ed3f6db21469edd531a85f8084 Mon Sep 17 00:00:00 2001 From: Thomas Keller Date: Mon, 30 Aug 2010 09:00:20 +0200 Subject: [PATCH 07/15] simplify and harden the configuration file writeouts, didn't knew file_put_contents was around for so long already... --- src/IDF/Plugin/SyncMonotone.php | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/IDF/Plugin/SyncMonotone.php b/src/IDF/Plugin/SyncMonotone.php index 6f37fb8..4819db3 100644 --- a/src/IDF/Plugin/SyncMonotone.php +++ b/src/IDF/Plugin/SyncMonotone.php @@ -157,10 +157,11 @@ end END; $rcfile = $projectpath.'/monotonerc'; - // FIXME: sanity - $fp = fopen($rcfile, 'w'); - fwrite($fp, $monotonerc); - fclose($fp); + if (!file_put_contents($rcfile, $monotonerc, LOCK_EX)) { + throw new IDF_Scm_Exception(sprintf( + __('Could not write mtn configuration file "%s"'), $rcfile) + )); + } // // step 4) read in and append the usher config with the new server @@ -205,10 +206,13 @@ END; $parsed_config[] = $new_server; $usher_rc = IDF_Scm_Monotone_BasicIO::compile($parsed_config); - // FIXME: more sanity - what happens on failing writes? - $fp = fopen($usher_config, 'w'); - fwrite($fp, $usher_rc); - fclose($fp); + // FIXME: more sanity - what happens on failing writes? we do not + // have a backup copy of usher.conf around... + if (!file_put_contents($usher_config, $usher_rc, LOCK_EX)) { + throw new IDF_Scm_Exception(sprintf( + __('Could not write usher configuration file "%s"'), $usher_config) + )); + } // // step 5) reload usher to pick up the new configuration From 82aaf43d5d612a04b3fd365e0ccbd0e0dd3b86d5 Mon Sep 17 00:00:00 2001 From: Thomas Keller Date: Mon, 30 Aug 2010 11:20:47 +0000 Subject: [PATCH 08/15] - initial work on a mtn-post-push script which updates IDF's timeline when new revisions arrive. this still needs some more tests, but its a start. - refactor out the monotonerc template from SyncMonotone.php and place it in a separate template file (access control hooks are still missing from there) --- scripts/mtn-post-push | 23 +++++++ scripts/mtnpostpush.php | 63 ++++++++++++++++++ src/IDF/Plugin/SyncMonotone.php | 74 ++++++++++++++-------- src/IDF/Plugin/SyncMonotone/monotonerc.tpl | 71 +++++++++++++++++++++ src/IDF/relations.php | 3 + 5 files changed, 209 insertions(+), 25 deletions(-) create mode 100755 scripts/mtn-post-push create mode 100644 scripts/mtnpostpush.php create mode 100644 src/IDF/Plugin/SyncMonotone/monotonerc.tpl diff --git a/scripts/mtn-post-push b/scripts/mtn-post-push new file mode 100755 index 0000000..46f5d3b --- /dev/null +++ b/scripts/mtn-post-push @@ -0,0 +1,23 @@ +#!/bin/sh +# +# This hook informs IDF that new revisions arrived in the database +# of the specified project. +# +# This hook is normally installed automatically at the creation of your +# repository if you have everything configured correctly. If you want +# to enable it later, you need to call it into your monotonerc file +# from the hook "note_netsync_end". (See chapter "Event Notifications +# and Triggers" on .) +# + +dir=$(dirname "$0") +res=$(cd "$dir" && /bin/pwd || "$dir") +SCRIPTDIR="$res/$(readlink $0)" +PHP_POST_PUSH=$SCRIPTDIR/mtnpostpush.php + +base=$(basename "$0") +TMPFILE=$(mktemp /tmp/${tempfoo}.XXXXXX) || exit 1 +while read rev; do echo $rev >> $TMPFILE; done + +echo php $PHP_POST_PUSH "$1" \< $TMPFILE \&\& rm -f $TMPFILE |\ + at now > /dev/null 2>&1 diff --git a/scripts/mtnpostpush.php b/scripts/mtnpostpush.php new file mode 100644 index 0000000..0fc9e02 --- /dev/null +++ b/scripts/mtnpostpush.php @@ -0,0 +1,63 @@ + 'name-of-the-project', + * 'revisions' => array('123abc...', '456def...', ...)); + * + */ + +fwrite(STDERR, "waiting for revisions on STDIN...\n"); +$stdin = file_get_contents('php://stdin'); + +$params = array('project' => $argv[1], + 'revisions' => explode("\n", chop($stdin))); +Pluf_Signal::send('mtnpostpush.php::run', 'mtnpostpush.php', $params); + + + diff --git a/src/IDF/Plugin/SyncMonotone.php b/src/IDF/Plugin/SyncMonotone.php index 4819db3..8ff4218 100644 --- a/src/IDF/Plugin/SyncMonotone.php +++ b/src/IDF/Plugin/SyncMonotone.php @@ -3,7 +3,7 @@ /* # ***** BEGIN LICENSE BLOCK ***** # This file is part of InDefero, an open source project management application. -# Copyright (C) 2008 Céondo Ltd and contributors. +# Copyright (C) 2010 Céondo Ltd and contributors. # # InDefero is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -37,6 +37,9 @@ class IDF_Plugin_SyncMonotone case 'IDF_Project::created': $plug->processMonotoneCreate($params['project']); break; + case 'mtnpostpush.php::run': + $plug->processSyncTimeline($params); + break; } } @@ -73,6 +76,13 @@ class IDF_Plugin_SyncMonotone ); } + $mtnpostpush = realpath(dirname(__FILE__) . "/../../../scripts/mtn-post-push"); + if (!file_exists($mtnpostpush)) { + throw new IDF_Scm_Exception(sprintf( + __('Could not find mtn-post-push script "%s".'), $mtnpostpush + )); + } + $shortname = $project->shortname; $projectpath = sprintf($projecttempl, $shortname); if (file_exists($projectpath)) { @@ -132,34 +142,19 @@ class IDF_Plugin_SyncMonotone // // step 3) write monotonerc for access control // FIXME: netsync access control is still missing! - // - $monotonerc =<<id + )); + IDF_Scm::syncTimeline($project, true); + Pluf_Log::event(array( + 'IDF_Plugin_SyncMonotone::processSyncTimeline', + 'sync', array($pname, $project->id) + )); + } } diff --git a/src/IDF/Plugin/SyncMonotone/monotonerc.tpl b/src/IDF/Plugin/SyncMonotone/monotonerc.tpl new file mode 100644 index 0000000..6c74813 --- /dev/null +++ b/src/IDF/Plugin/SyncMonotone/monotonerc.tpl @@ -0,0 +1,71 @@ +-- ***** BEGIN LICENSE BLOCK ***** +-- This file is part of InDefero, an open source project management application. +-- Copyright (C) 2008 Céondo Ltd and contributors. +-- +-- InDefero is free software; you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation; either version 2 of the License, or +-- (at your option) any later version. +-- +-- InDefero is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program; if not, write to the Free Software +-- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +-- +-- ***** END LICENSE BLOCK ***** + +-- +-- controls the access rights for remote_stdio which is used by IDFs frontend +-- +function get_remote_automate_permitted(key_identity, command, options) + local read_only_commands = { + "get_corresponding_path", "get_content_changed", "tags", "branches", + "common_ancestors", "packet_for_fdelta", "packet_for_fdata", + "packets_for_certs", "packet_for_rdata", "get_manifest_of", + "get_revision", "select", "graph", "children", "parents", "roots", + "leaves", "ancestry_difference", "toposort", "erase_ancestors", + "descendents", "ancestors", "heads", "get_file_of", "get_file", + "interface_version", "get_attributes", "content_diff", + "file_merge", "show_conflicts", "certs", "keys" + } + + for _,v in ipairs(read_only_commands) do + if (v == command[1]) then + return true + end + end + + return false +end + +_idf_revs = {} +function note_netsync_start(session_id) + _idf_revs[session_id] = {} +end + +function note_netsync_revision_received(new_id, revision, certs, session_id) + table.insert(_idf_revs[session_id], new_id) +end + +function note_netsync_end (session_id, ...) + if table.getn(_idf_revs[session_id]) == 0 then + return + end + + local pin,pout,pid = spawn_pipe("%%MTNPOSTPUSH%%", "%%PROJECT%%"); + if pid == -1 then + print("could execute %%MTNPOSTPUSH%%") + return + end + + for _,r in ipairs(_idf_revs[session_id]) do + pin:write(r .. "\n") + end + pin:close() + + wait(pid) +end diff --git a/src/IDF/relations.php b/src/IDF/relations.php index bf4e32e..6734515 100644 --- a/src/IDF/relations.php +++ b/src/IDF/relations.php @@ -88,6 +88,9 @@ Pluf_Signal::connect('gitpostupdate.php::run', # monotone synchronization Pluf_Signal::connect('IDF_Project::created', array('IDF_Plugin_SyncMonotone', 'entry')); +Pluf_Signal::connect('phppostpush.php::run', + array('IDF_Plugin_SyncMonotone', 'entry')); + # # -- Processing of the webhook queue -- Pluf_Signal::connect('queuecron.php::run', From e789263068a7e5fd9d2d67ad3a8099108178cdb3 Mon Sep 17 00:00:00 2001 From: Thomas Keller Date: Mon, 30 Aug 2010 12:58:52 +0000 Subject: [PATCH 09/15] - raise required mtn version to 0.99 - add '--key=' to the default mtn arguments in idf.php-dist --- doc/readme-monotone.mdtext | 5 ++--- src/IDF/conf/idf.php-dist | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/doc/readme-monotone.mdtext b/doc/readme-monotone.mdtext index 2e54367..cbaf8ba 100644 --- a/doc/readme-monotone.mdtext +++ b/doc/readme-monotone.mdtext @@ -3,9 +3,8 @@ ## general This version of indefero contains an implementation of the monotone - automation interface. It needs at least monotone version 0.47 - (interface version 12.0) or newer, but as development continues, its - likely that this dependency has to be raised. + automation interface. It needs at least monotone version 0.99 + (interface version 13.0) or newer. To set up a new IDF project with monotone quickly, all you need to do is to create a new monotone database with diff --git a/src/IDF/conf/idf.php-dist b/src/IDF/conf/idf.php-dist index 57d3e95..9339a5a 100644 --- a/src/IDF/conf/idf.php-dist +++ b/src/IDF/conf/idf.php-dist @@ -73,10 +73,10 @@ $cfg['git_write_remote_url'] = 'git@localhost:%s.git'; $cfg['svn_repositories'] = 'file:///home/svn/repositories/%s'; $cfg['svn_remote_url'] = 'http://localhost/svn/%s'; -# Path to the monotone binary +# Path to the monotone binary (you need mtn 0.99 or newer) $cfg['mtn_path'] = 'mtn'; # Additional options for the started monotone process -$cfg['mtn_opts'] = array('--no-workspace', '--norc'); +$cfg['mtn_opts'] = array('--no-workspace', '--no-standard-rcfiles', '--key='); # # You can setup monotone for use with indefero in several ways. The # two most-used should be: From 6b4abac08ea0bec6c47cea052b1a1fb7faaa349e Mon Sep 17 00:00:00 2001 From: Thomas Keller Date: Mon, 30 Aug 2010 13:46:05 +0000 Subject: [PATCH 10/15] - PHP doesn't like the $var = kind of initialization and won't return variable references properly, so revert that change again - since we're requiring 0.99 now, we also have to use au generate_key instead of au genkey --- src/IDF/Plugin/SyncMonotone.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/IDF/Plugin/SyncMonotone.php b/src/IDF/Plugin/SyncMonotone.php index 8ff4218..ecbb301 100644 --- a/src/IDF/Plugin/SyncMonotone.php +++ b/src/IDF/Plugin/SyncMonotone.php @@ -106,7 +106,8 @@ class IDF_Plugin_SyncMonotone escapeshellarg($dbfile) ); $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd; - $ll = exec($cmd, $output = array(), $return = 0); + $output = $return = null; + $ll = exec($cmd, $output, $return); if ($return != 0) { throw new IDF_Scm_Exception(sprintf( __('The database file %s could not be created.'), $dbfile @@ -127,12 +128,14 @@ class IDF_Plugin_SyncMonotone $keyname = $shortname.'-server@'.$server; $cmd = sprintf( - Pluf::f('mtn_path', 'mtn').' au genkey --confdir=%s %s ""', + Pluf::f('mtn_path', 'mtn').' au generate_key --confdir=%s %s ""', escapeshellarg($projectpath), escapeshellarg($keyname) ); + $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd; - $ll = exec($cmd, $output = array(), $return = 0); + $output = $return = null; + $ll = exec($cmd, $output, $return); if ($return != 0) { throw new IDF_Scm_Exception(sprintf( __('The server key %s could not be created.'), $keyname From a384c6093737782c8a880b936e94203152590200 Mon Sep 17 00:00:00 2001 From: Thomas Keller Date: Mon, 30 Aug 2010 20:14:03 +0000 Subject: [PATCH 11/15] fix a PHP notice --- src/IDF/Scm/Monotone/Stdio.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/IDF/Scm/Monotone/Stdio.php b/src/IDF/Scm/Monotone/Stdio.php index 391133a..b447955 100644 --- a/src/IDF/Scm/Monotone/Stdio.php +++ b/src/IDF/Scm/Monotone/Stdio.php @@ -146,8 +146,9 @@ class IDF_Scm_Monotone_Stdio return false; $read = array($this->pipes[1], $this->pipes[2]); + $write = $except = null; $streamsChanged = stream_select( - $read, $write = null, $except = null, 0, 20000 + $read, $write, $except, 0, 20000 ); if ($streamsChanged === false) { From e26a5c8cdfc07e5b3aa7044a0d878994761d0fcc Mon Sep 17 00:00:00 2001 From: Thomas Keller Date: Mon, 30 Aug 2010 21:29:42 +0000 Subject: [PATCH 12/15] waiting usher instances should be stoppable as well --- src/IDF/templates/idf/gadmin/usher/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IDF/templates/idf/gadmin/usher/index.html b/src/IDF/templates/idf/gadmin/usher/index.html index c81c134..fa6041b 100644 --- a/src/IDF/templates/idf/gadmin/usher/index.html +++ b/src/IDF/templates/idf/gadmin/usher/index.html @@ -14,7 +14,7 @@ {$server.name} {$server.status} - {if preg_match("/ACTIVE|RUNNING|SLEEPING/", $server.status)} + {if preg_match("/ACTIVE|WAITING|RUNNING|SLEEPING/", $server.status)} {trans 'stop'} {elseif $server.status == "STOPPED"} From 187365db76c3eb5a35f12ec636d9cc3028e8059d Mon Sep 17 00:00:00 2001 From: Thomas Keller Date: Mon, 30 Aug 2010 21:50:25 +0000 Subject: [PATCH 13/15] IDF_Scm::isValidRevision() only ever takes one argument --- src/IDF/Views/Source.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IDF/Views/Source.php b/src/IDF/Views/Source.php index 37d9ca9..18d79a2 100644 --- a/src/IDF/Views/Source.php +++ b/src/IDF/Views/Source.php @@ -181,7 +181,7 @@ class IDF_Views_Source $request_file)); return new Pluf_HTTP_Response_Redirect($url, 301); } - if (!$scm->isValidRevision($commit, $request_file)) { + if (!$scm->isValidRevision($commit)) { // Redirect to the first branch return new Pluf_HTTP_Response_Redirect($fburl); } From 5f4e1da0c862e18515c5ddbaa6ccdb88852e7411 Mon Sep 17 00:00:00 2001 From: Thomas Keller Date: Tue, 31 Aug 2010 21:07:31 +0000 Subject: [PATCH 14/15] while there _should_ be really a custom application exception, currently there is no such one --- src/IDF/Key.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/IDF/Key.php b/src/IDF/Key.php index 0d31ba3..c9d7eff 100644 --- a/src/IDF/Key.php +++ b/src/IDF/Key.php @@ -84,7 +84,7 @@ class IDF_Key extends Pluf_Model return array('ssh', $m[2], $m[1]); } - throw new IDF_Exception('invalid or unknown key data detected'); + throw new Exception('invalid or unknown key data detected'); } /** @@ -123,7 +123,7 @@ class IDF_Key extends Pluf_Model { list($type, $keyName, $keyData) = $this->parseContent(); if ($type != 'mtn') - throw new IDF_Exception('key is not a monotone public key'); + throw new Exception('key is not a monotone public key'); return sha1($keyName.":".$keyData); } From 5d263e78e00dd3d5569cc23f81c9279fdad81385 Mon Sep 17 00:00:00 2001 From: Thomas Keller Date: Tue, 31 Aug 2010 21:17:09 +0000 Subject: [PATCH 15/15] its dateago:"without", not dateago:"wihtout" --- src/IDF/templates/idf/source/changelog.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/IDF/templates/idf/source/changelog.html b/src/IDF/templates/idf/source/changelog.html index 971d3ba..8c56074 100644 --- a/src/IDF/templates/idf/source/changelog.html +++ b/src/IDF/templates/idf/source/changelog.html @@ -12,12 +12,12 @@ {foreach $changes as $change} {aurl 'url', 'IDF_Views_Source::commit', array($project.shortname, $change.scm_id)} -{$change.creation_dtime|dateago:"wihtout"} +{$change.creation_dtime|dateago:"without"} {issuetext $change.summary, $request}{if $change.fullmessage}

{issuetext $change.fullmessage, $request, true, false, true, true, true}{/if} -
{trans 'Commit'} {$change.scm_id}, +
{trans 'Commit'} {$change.scm_id}, {trans 'by'} {showuser $change.get_author(), $request, $change.origauthor}