Merge branch 'master' of github.com:tommyd3mdi/indefero-monotone
This commit is contained in:
commit
a442fd588e
128
README
128
README
@ -1,128 +0,0 @@
|
|||||||
monotone implementation notes
|
|
||||||
-----------------------------
|
|
||||||
|
|
||||||
1. general
|
|
||||||
|
|
||||||
This branch contains an implementation of the monotone automation interface.
|
|
||||||
It needs at least monotone version 0.47 (interface version 12.0) or
|
|
||||||
newer. To set up a new project with monotone, all you need to do is
|
|
||||||
to create a new monotone database with
|
|
||||||
|
|
||||||
$ mtn db init -d project.mtn
|
|
||||||
|
|
||||||
in the configured repository path ('mtn_repositories'). To have a really
|
|
||||||
workable setup, this database needs an initial commit on the configured
|
|
||||||
master branch of the project. This can be done easily with
|
|
||||||
|
|
||||||
$ mkdir tmp && touch tmp/remove_me
|
|
||||||
$ mtn import -d project.mtn -b master.branch.name \
|
|
||||||
-m "initial commit" tmp
|
|
||||||
$ rm -rf tmp
|
|
||||||
|
|
||||||
|
|
||||||
2. current state / internals
|
|
||||||
|
|
||||||
The implementation should be fairly stable and fast, though some
|
|
||||||
information, such as individual file sizes or last change information,
|
|
||||||
won't scale well with the tree size. Its expected that the mtn
|
|
||||||
automation interface improves in this area in the future and that
|
|
||||||
these parts can then be rewritten with speed in mind.
|
|
||||||
|
|
||||||
Another area of improvement is the access pattern to the monotone
|
|
||||||
database. While only one process is started per request, the time
|
|
||||||
(and server resource) penalty for this could still be dramatic once
|
|
||||||
many clients try to access the service. Luckily, monotone has an
|
|
||||||
easy way to deliver its stdio protocol for automation usage over the
|
|
||||||
network (mtn au remote_stdio), so the following scenarios are possible:
|
|
||||||
|
|
||||||
a) setup a single mtn server serving one database on a different
|
|
||||||
(faster) server and let the stdio client connect to that
|
|
||||||
|
|
||||||
b) setup usher (available from branch net.venge.monotone.contrib.usher
|
|
||||||
from the official mtn repository on monotone.ca) as proxy in
|
|
||||||
front of several local monotone databases mirroring themselves
|
|
||||||
|
|
||||||
c) like b), but use usher as proxy in front of several other remote
|
|
||||||
monotone databases (forwarding)
|
|
||||||
|
|
||||||
The scenario in a) might be needed anyways for a shared hosting
|
|
||||||
environment, because a database which gets served via netsync cannot
|
|
||||||
be accessed by another local process at the same time (its locked then),
|
|
||||||
so ideally both, the network functionality as well as the indefero
|
|
||||||
browsing functionality should be delivered from one single database
|
|
||||||
per project via netsync.
|
|
||||||
|
|
||||||
The only alternative for this setup is a two-database approach, where one
|
|
||||||
database acts as network node and the other as backend for indefero.
|
|
||||||
The synchronization between these two would then have to happen via
|
|
||||||
standard tools (cron...) or a sync request from one database to the other.
|
|
||||||
|
|
||||||
While the current implementation is ready for the two database approach,
|
|
||||||
some code parts and configuration changes have to happen for the remote
|
|
||||||
stdio usage. Bascially this is replacing the initial call to
|
|
||||||
|
|
||||||
mtn -d project.mtn au stdio (Monotone.php, around line 74)
|
|
||||||
|
|
||||||
with
|
|
||||||
|
|
||||||
mtn au remote_stdio HOSTNAME
|
|
||||||
|
|
||||||
which could be made configurable in conf/idf.php. But again, this heavily
|
|
||||||
depends on the exact anticipated server setup.
|
|
||||||
|
|
||||||
To scale things up a bit, multiple projects should of course use
|
|
||||||
separated databases. The main reason for that is that while read access
|
|
||||||
can be granted on a branch level, write access gives total write
|
|
||||||
possibilities on the whole database. One approach would be to start
|
|
||||||
one serve process for each database, but the obvious downside here is
|
|
||||||
that each of those processes would need to get bound to another
|
|
||||||
(non-standard) port making it hard for users to "just clone" the
|
|
||||||
project sources without knowing the exact port.
|
|
||||||
|
|
||||||
Usher comes to the rescue here as well. It has three ways
|
|
||||||
to recognize the request for a particular database:
|
|
||||||
|
|
||||||
a) by looking at the requested host name (similar to SNI for Apache)
|
|
||||||
|
|
||||||
b) by evaluating the requested branch pattern
|
|
||||||
|
|
||||||
c) by evaluating the path part from an mtn:// uri (new in mtn 0.48)
|
|
||||||
|
|
||||||
The best way is probably to configure it with c) - instead of pulling
|
|
||||||
a project like this
|
|
||||||
|
|
||||||
$ mtn pull hostname branchname
|
|
||||||
|
|
||||||
a user uses the URI syntax (which will, btw. be the default from
|
|
||||||
mtn 0.99 onwards):
|
|
||||||
|
|
||||||
$ mtn pull mtn://hostname/database?branchname
|
|
||||||
|
|
||||||
Here, the "/database" part is used by usher to determine which backend
|
|
||||||
database should be used for the network action. The "clone" command
|
|
||||||
will also support this mtn:// uri syntax, but this didn't made it into
|
|
||||||
0.48, but will be available from 0.99 and later.
|
|
||||||
|
|
||||||
|
|
||||||
3. indefero critique:
|
|
||||||
|
|
||||||
It was not always 100% clear what some of the abstract SCM API method
|
|
||||||
wanted in return. While it helped a lot to have prior art in form of the
|
|
||||||
SVN and git implementation, the documentation of the abstract IDF_Scm
|
|
||||||
should probably still be improved.
|
|
||||||
|
|
||||||
Since branch and tag names can be of arbitrary size, it was not possible
|
|
||||||
to display them completely in the default layout. This might be a problem
|
|
||||||
in other SCMs as well, in particular for the monotone implementation I
|
|
||||||
introduced a special filter, called "IDF_Views_Source_ShortenString".
|
|
||||||
|
|
||||||
The API methods getPathInfo() and getTree() return similar VCS "objects"
|
|
||||||
which unfortunately do not have a well-defined structure - this should
|
|
||||||
probably addressed in future indefero releases.
|
|
||||||
|
|
||||||
While the returned objects from getTree() contain all the needed
|
|
||||||
information, indefero doesn't seem to use them to sort the output
|
|
||||||
f.e. alphabetically or in such a way that directories are outputted
|
|
||||||
before files. It was unclear if the SCM implementor should do this
|
|
||||||
task or not and what the admired default sorting should be.
|
|
||||||
|
|
63
doc/readme-monotone.mdtext
Normal file
63
doc/readme-monotone.mdtext
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
# monotone implementation notes
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
To set up a new IDF project with monotone quickly, all you need to do
|
||||||
|
is to create a new monotone database with
|
||||||
|
|
||||||
|
$ mtn db init -d project.mtn
|
||||||
|
|
||||||
|
in the configured repository path `$cfg['mtn_repositories']` and
|
||||||
|
configure `$cfg['mtn_db_access']` to "local".
|
||||||
|
|
||||||
|
To have a really workable setup, this database needs an initial commit
|
||||||
|
on the configured master branch of the project. This can be done easily
|
||||||
|
with
|
||||||
|
|
||||||
|
$ mkdir tmp && touch tmp/remove_me
|
||||||
|
$ mtn import -d project.mtn -b master.branch.name \
|
||||||
|
-m "initial commit" tmp
|
||||||
|
$ rm -rf tmp
|
||||||
|
|
||||||
|
Its expected that more scripts arrive soon to automate this and other
|
||||||
|
tasks in the future for (multi)forge setups.
|
||||||
|
|
||||||
|
## current state / internals
|
||||||
|
|
||||||
|
The implementation should be fairly stable and fast, though some
|
||||||
|
information, such as individual file sizes or last change information,
|
||||||
|
won't scale well with the tree size. Its expected that the mtn
|
||||||
|
automation interface improves in this area in the future and that
|
||||||
|
these parts can then be rewritten with speed in mind.
|
||||||
|
|
||||||
|
As the idf.conf-dist explains more in detail, different access patterns
|
||||||
|
are possible to retrieve changeset data from monotone. Please refer
|
||||||
|
to the documentation there for more information.
|
||||||
|
|
||||||
|
## indefero critique:
|
||||||
|
|
||||||
|
It was not always 100% clear what some of the abstract SCM API method
|
||||||
|
wanted in return. While it helped a lot to have prior art in form of the
|
||||||
|
SVN and git implementation, the documentation of the abstract IDF_Scm
|
||||||
|
should probably still be improved.
|
||||||
|
|
||||||
|
Since branch and tag names can be of arbitrary size, it was not possible
|
||||||
|
to display them completely in the default layout. This might be a problem
|
||||||
|
in other SCMs as well, in particular for the monotone implementation I
|
||||||
|
introduced a special filter, called "IDF_Views_Source_ShortenString".
|
||||||
|
|
||||||
|
The API methods getPathInfo() and getTree() return similar VCS "objects"
|
||||||
|
which unfortunately do not have a well-defined structure - this should
|
||||||
|
probably addressed in future indefero releases.
|
||||||
|
|
||||||
|
While the returned objects from getTree() contain all the needed
|
||||||
|
information, indefero doesn't seem to use them to sort the output
|
||||||
|
f.e. alphabetically or in such a way that directories are outputted
|
||||||
|
before files. It was unclear if the SCM implementor should do this
|
||||||
|
task or not and what the admired default sorting should be.
|
||||||
|
|
@ -210,32 +210,27 @@ class IDF_Form_UserAccount extends Pluf_Form
|
|||||||
public static function checkPublicKey($key, $type, $user=0)
|
public static function checkPublicKey($key, $type, $user=0)
|
||||||
{
|
{
|
||||||
$key = trim($key);
|
$key = trim($key);
|
||||||
if (strlen($key) == 0)
|
if (strlen($key) == 0) {
|
||||||
{
|
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($type == 'ssh')
|
if ($type == 'ssh') {
|
||||||
{
|
|
||||||
$key = str_replace(array("\n", "\r"), '', $key);
|
$key = str_replace(array("\n", "\r"), '', $key);
|
||||||
if (!preg_match('#^ssh\-[a-z]{3}\s\S+\s\S+$#', $key))
|
if (!preg_match('#^ssh\-[a-z]{3}\s\S+\s\S+$#', $key)) {
|
||||||
{
|
|
||||||
throw new Pluf_Form_Invalid(
|
throw new Pluf_Form_Invalid(
|
||||||
__('The format of the key is not valid. It must start '.
|
__('The format of the key is not valid. It must start '.
|
||||||
'with ssh-dss or ssh-rsa, a long string on a single '.
|
'with ssh-dss or ssh-rsa, a long string on a single '.
|
||||||
'line and at the end a comment.')
|
'line and at the end a comment.')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (Pluf::f('idf_strong_key_check', false))
|
if (Pluf::f('idf_strong_key_check', false)) {
|
||||||
{
|
|
||||||
$tmpfile = Pluf::f('tmp_folder', '/tmp').'/'.$user.'-key';
|
$tmpfile = Pluf::f('tmp_folder', '/tmp').'/'.$user.'-key';
|
||||||
file_put_contents($tmpfile, $key, LOCK_EX);
|
file_put_contents($tmpfile, $key, LOCK_EX);
|
||||||
$cmd = Pluf::f('idf_exec_cmd_prefix', '').
|
$cmd = Pluf::f('idf_exec_cmd_prefix', '').
|
||||||
'ssh-keygen -l -f '.escapeshellarg($tmpfile).' > /dev/null 2>&1';
|
'ssh-keygen -l -f '.escapeshellarg($tmpfile).' > /dev/null 2>&1';
|
||||||
exec($cmd, $out, $return);
|
exec($cmd, $out, $return);
|
||||||
unlink($tmpfile);
|
unlink($tmpfile);
|
||||||
if ($return != 0)
|
if ($return != 0) {
|
||||||
{
|
|
||||||
throw new Pluf_Form_Invalid(
|
throw new Pluf_Form_Invalid(
|
||||||
__('Please check the key as it does not appears '.
|
__('Please check the key as it does not appears '.
|
||||||
'to be a valid key.')
|
'to be a valid key.')
|
||||||
@ -243,18 +238,15 @@ class IDF_Form_UserAccount extends Pluf_Form
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ($type == 'mtn')
|
else if ($type == 'mtn') {
|
||||||
{
|
if (!preg_match('#^\[pubkey [^\]]+\]\s*\S+\s*\[end\]$#', $key)) {
|
||||||
if (!preg_match('#^\[pubkey [^\]]+\]\s*\S+\s*\[end\]$#', $key))
|
|
||||||
{
|
|
||||||
throw new Pluf_Form_Invalid(
|
throw new Pluf_Form_Invalid(
|
||||||
__('The format of the key is not valid. It must start '.
|
__('The format of the key is not valid. It must start '.
|
||||||
'with [pubkey KEYNAME], contain a long string on a single '.
|
'with [pubkey KEYNAME], contain a long string on a single '.
|
||||||
'line and end with [end] in the final third line.')
|
'line and end with [end] in the final third line.')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (Pluf::f('idf_strong_key_check', false))
|
if (Pluf::f('idf_strong_key_check', false)) {
|
||||||
{
|
|
||||||
// if monotone can read it, it should be valid
|
// if monotone can read it, it should be valid
|
||||||
$mtn_opts = implode(' ', Pluf::f('mtn_opts', array()));
|
$mtn_opts = implode(' ', Pluf::f('mtn_opts', array()));
|
||||||
$cmd = Pluf::f('idf_exec_cmd_prefix', '').
|
$cmd = Pluf::f('idf_exec_cmd_prefix', '').
|
||||||
@ -264,8 +256,7 @@ class IDF_Form_UserAccount extends Pluf_Form
|
|||||||
fwrite($fp, $key);
|
fwrite($fp, $key);
|
||||||
$return = pclose($fp);
|
$return = pclose($fp);
|
||||||
|
|
||||||
if ($return != 0)
|
if ($return != 0) {
|
||||||
{
|
|
||||||
throw new Pluf_Form_Invalid(
|
throw new Pluf_Form_Invalid(
|
||||||
__('Please check the key as it does not appears '.
|
__('Please check the key as it does not appears '.
|
||||||
'to be a valid key.')
|
'to be a valid key.')
|
||||||
@ -279,15 +270,12 @@ class IDF_Form_UserAccount extends Pluf_Form
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If $user, then check if not the same key stored
|
// If $user, then check if not the same key stored
|
||||||
if ($user)
|
if ($user) {
|
||||||
{
|
|
||||||
$ruser = Pluf::factory('Pluf_User', $user);
|
$ruser = Pluf::factory('Pluf_User', $user);
|
||||||
if ($ruser->id > 0)
|
if ($ruser->id > 0) {
|
||||||
{
|
|
||||||
$sql = new Pluf_SQL('content=%s AND type=%s', array($key, $type));
|
$sql = new Pluf_SQL('content=%s AND type=%s', array($key, $type));
|
||||||
$keys = Pluf::factory('IDF_Key')->getList(array('filter' => $sql->gen()));
|
$keys = Pluf::factory('IDF_Key')->getList(array('filter' => $sql->gen()));
|
||||||
if (count($keys) > 0)
|
if (count($keys) > 0) {
|
||||||
{
|
|
||||||
throw new Pluf_Form_Invalid(
|
throw new Pluf_Form_Invalid(
|
||||||
__('You already have uploaded this key.')
|
__('You already have uploaded this key.')
|
||||||
);
|
);
|
||||||
|
@ -184,8 +184,7 @@ class IDF_Key extends Pluf_Model
|
|||||||
public static function getAvailableKeyTypes()
|
public static function getAvailableKeyTypes()
|
||||||
{
|
{
|
||||||
$key_types = array(__("SSH") => 'ssh');
|
$key_types = array(__("SSH") => 'ssh');
|
||||||
if (array_key_exists('mtn', Pluf::f('allowed_scm', array())))
|
if (array_key_exists('mtn', Pluf::f('allowed_scm', array()))) {
|
||||||
{
|
|
||||||
$key_types[__("monotone")] = 'mtn';
|
$key_types[__("monotone")] = 'mtn';
|
||||||
}
|
}
|
||||||
return $key_types;
|
return $key_types;
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
function IDF_Migrations_16KeyType_up($params=null)
|
function IDF_Migrations_16KeyType_up($params=null)
|
||||||
{
|
{
|
||||||
$table = Pluf::factory('IDF_Keys')->getSqlTable();
|
$table = Pluf::factory('IDF_Key')->getSqlTable();
|
||||||
$sql = array();
|
$sql = array();
|
||||||
$sql['PostgreSQL'] = 'ALTER TABLE '.$table.' ADD COLUMN "type" VARCHAR(3) DEFAULT \'ssh\'';
|
$sql['PostgreSQL'] = 'ALTER TABLE '.$table.' ADD COLUMN "type" VARCHAR(3) DEFAULT \'ssh\'';
|
||||||
$sql['MySQL'] = 'ALTER TABLE '.$table.' ADD COLUMN `type` VARCHAR(3) DEFAULT \'ssh\'';
|
$sql['MySQL'] = 'ALTER TABLE '.$table.' ADD COLUMN `type` VARCHAR(3) DEFAULT \'ssh\'';
|
||||||
@ -41,7 +41,7 @@ function IDF_Migrations_16KeyType_up($params=null)
|
|||||||
|
|
||||||
function IDF_Migrations_16KeyType_down($params=null)
|
function IDF_Migrations_16KeyType_down($params=null)
|
||||||
{
|
{
|
||||||
$table = Pluf::factory('IDF_Keys')->getSqlTable();
|
$table = Pluf::factory('IDF_Key')->getSqlTable();
|
||||||
$sql = array();
|
$sql = array();
|
||||||
$sql['PostgreSQL'] = 'ALTER TABLE '.$table.' DROP COLUMN "type"';
|
$sql['PostgreSQL'] = 'ALTER TABLE '.$table.' DROP COLUMN "type"';
|
||||||
$sql['MySQL'] = 'ALTER TABLE '.$table.' DROP COLUMN `type`';
|
$sql['MySQL'] = 'ALTER TABLE '.$table.' DROP COLUMN `type`';
|
||||||
|
@ -54,8 +54,7 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
// FIXME: this obviously won't work with remote databases - upstream
|
// FIXME: this obviously won't work with remote databases - upstream
|
||||||
// needs to implement mtn db info in automate at first
|
// needs to implement mtn db info in automate at first
|
||||||
$repo = sprintf(Pluf::f('mtn_repositories'), $this->project->shortname);
|
$repo = sprintf(Pluf::f('mtn_repositories'), $this->project->shortname);
|
||||||
if (!file_exists($repo))
|
if (!file_exists($repo)) {
|
||||||
{
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,7 +73,7 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
$out = $this->stdio->exec(array("interface_version"));
|
$out = $this->stdio->exec(array('interface_version'));
|
||||||
return floatval($out) >= self::$MIN_INTERFACE_VERSION;
|
return floatval($out) >= self::$MIN_INTERFACE_VERSION;
|
||||||
}
|
}
|
||||||
catch (IDF_Scm_Exception $e) {}
|
catch (IDF_Scm_Exception $e) {}
|
||||||
@ -92,15 +91,14 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
}
|
}
|
||||||
// FIXME: we could / should introduce handling of suspended
|
// FIXME: we could / should introduce handling of suspended
|
||||||
// (i.e. dead) branches here by hiding them from the user's eye...
|
// (i.e. dead) branches here by hiding them from the user's eye...
|
||||||
$out = $this->stdio->exec(array("branches"));
|
$out = $this->stdio->exec(array('branches'));
|
||||||
|
|
||||||
// note: we could expand each branch with one of its head revisions
|
// note: we could expand each branch with one of its head revisions
|
||||||
// here, but these would soon become bogus anyway and we cannot
|
// here, but these would soon become bogus anyway and we cannot
|
||||||
// map multiple head revisions here either, so we just use the
|
// map multiple head revisions here either, so we just use the
|
||||||
// selector as placeholder
|
// selector as placeholder
|
||||||
$res = array();
|
$res = array();
|
||||||
foreach (preg_split("/\n/", $out, -1, PREG_SPLIT_NO_EMPTY) as $b)
|
foreach (preg_split("/\n/", $out, -1, PREG_SPLIT_NO_EMPTY) as $b) {
|
||||||
{
|
|
||||||
$res["h:$b"] = $b;
|
$res["h:$b"] = $b;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,8 +121,7 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
$branch = "*";
|
$branch = "*";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count($this->_resolveSelector("h:$branch")) == 0)
|
if (count($this->_resolveSelector("h:$branch")) == 0) {
|
||||||
{
|
|
||||||
throw new IDF_Scm_Exception(
|
throw new IDF_Scm_Exception(
|
||||||
"Branch $branch is empty"
|
"Branch $branch is empty"
|
||||||
);
|
);
|
||||||
@ -142,7 +139,7 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
*/
|
*/
|
||||||
private function _resolveSelector($selector)
|
private function _resolveSelector($selector)
|
||||||
{
|
{
|
||||||
$out = $this->stdio->exec(array("select", $selector));
|
$out = $this->stdio->exec(array('select', $selector));
|
||||||
return preg_split("/\n/", $out, -1, PREG_SPLIT_NO_EMPTY);
|
return preg_split("/\n/", $out, -1, PREG_SPLIT_NO_EMPTY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,16 +154,13 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
$pos = 0;
|
$pos = 0;
|
||||||
$stanzas = array();
|
$stanzas = array();
|
||||||
|
|
||||||
while ($pos < strlen($in))
|
while ($pos < strlen($in)) {
|
||||||
{
|
|
||||||
$stanza = array();
|
$stanza = array();
|
||||||
while ($pos < strlen($in))
|
while ($pos < strlen($in)) {
|
||||||
{
|
|
||||||
if ($in[$pos] == "\n") break;
|
if ($in[$pos] == "\n") break;
|
||||||
|
|
||||||
$stanzaLine = array("key" => "", "values" => array(), "hash" => null);
|
$stanzaLine = array('key' => '', 'values' => array(), 'hash' => null);
|
||||||
while ($pos < strlen($in))
|
while ($pos < strlen($in)) {
|
||||||
{
|
|
||||||
$ch = $in[$pos];
|
$ch = $in[$pos];
|
||||||
if ($ch == '"' || $ch == '[') break;
|
if ($ch == '"' || $ch == '[') break;
|
||||||
++$pos;
|
++$pos;
|
||||||
@ -174,8 +168,7 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
$stanzaLine['key'] .= $ch;
|
$stanzaLine['key'] .= $ch;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($in[$pos] == '[')
|
if ($in[$pos] == '[') {
|
||||||
{
|
|
||||||
++$pos; // opening square bracket
|
++$pos; // opening square bracket
|
||||||
$stanzaLine['hash'] = substr($in, $pos, 40);
|
$stanzaLine['hash'] = substr($in, $pos, 40);
|
||||||
$pos += 40;
|
$pos += 40;
|
||||||
@ -184,12 +177,10 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
$valCount = 0;
|
$valCount = 0;
|
||||||
while ($in[$pos] == '"')
|
while ($in[$pos] == '"') {
|
||||||
{
|
|
||||||
++$pos; // opening quote
|
++$pos; // opening quote
|
||||||
$stanzaLine['values'][$valCount] = "";
|
$stanzaLine['values'][$valCount] = '';
|
||||||
while ($pos < strlen($in))
|
while ($pos < strlen($in)) {
|
||||||
{
|
|
||||||
$ch = $in[$pos]; $pr = $in[$pos-1];
|
$ch = $in[$pos]; $pr = $in[$pos-1];
|
||||||
if ($ch == '"' && $pr != '\\') break;
|
if ($ch == '"' && $pr != '\\') break;
|
||||||
++$pos;
|
++$pos;
|
||||||
@ -197,15 +188,13 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
}
|
}
|
||||||
++$pos; // closing quote
|
++$pos; // closing quote
|
||||||
|
|
||||||
if ($in[$pos] == ' ')
|
if ($in[$pos] == ' ') {
|
||||||
{
|
|
||||||
++$pos; // space
|
++$pos; // space
|
||||||
++$valCount;
|
++$valCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for ($i = 0; $i <= $valCount; $i++)
|
for ($i = 0; $i <= $valCount; $i++) {
|
||||||
{
|
|
||||||
$stanzaLine['values'][$i] = str_replace(
|
$stanzaLine['values'][$i] = str_replace(
|
||||||
array("\\\\", "\\\""),
|
array("\\\\", "\\\""),
|
||||||
array("\\", "\""),
|
array("\\", "\""),
|
||||||
@ -234,28 +223,22 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
{
|
{
|
||||||
static $certCache = array();
|
static $certCache = array();
|
||||||
|
|
||||||
if (!array_key_exists($rev, $certCache))
|
if (!array_key_exists($rev, $certCache)) {
|
||||||
{
|
$out = $this->stdio->exec(array('certs', $rev));
|
||||||
$out = $this->stdio->exec(array("certs", $rev));
|
|
||||||
|
|
||||||
$stanzas = self::_parseBasicIO($out);
|
$stanzas = self::_parseBasicIO($out);
|
||||||
$certs = array();
|
$certs = array();
|
||||||
foreach ($stanzas as $stanza)
|
foreach ($stanzas as $stanza) {
|
||||||
{
|
|
||||||
$certname = null;
|
$certname = null;
|
||||||
foreach ($stanza as $stanzaline)
|
foreach ($stanza as $stanzaline) {
|
||||||
{
|
|
||||||
// luckily, name always comes before value
|
// luckily, name always comes before value
|
||||||
if ($stanzaline['key'] == "name")
|
if ($stanzaline['key'] == 'name') {
|
||||||
{
|
|
||||||
$certname = $stanzaline['values'][0];
|
$certname = $stanzaline['values'][0];
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($stanzaline['key'] == "value")
|
if ($stanzaline['key'] == 'value') {
|
||||||
{
|
if (!array_key_exists($certname, $certs)) {
|
||||||
if (!array_key_exists($certname, $certs))
|
|
||||||
{
|
|
||||||
$certs[$certname] = array();
|
$certs[$certname] = array();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,13 +265,11 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
private function _getUniqueCertValuesFor($revs, $certName, $prefix)
|
private function _getUniqueCertValuesFor($revs, $certName, $prefix)
|
||||||
{
|
{
|
||||||
$certValues = array();
|
$certValues = array();
|
||||||
foreach ($revs as $rev)
|
foreach ($revs as $rev) {
|
||||||
{
|
|
||||||
$certs = $this->_getCerts($rev);
|
$certs = $this->_getCerts($rev);
|
||||||
if (!array_key_exists($certName, $certs))
|
if (!array_key_exists($certName, $certs))
|
||||||
continue;
|
continue;
|
||||||
foreach ($certs[$certName] as $certValue)
|
foreach ($certs[$certName] as $certValue) {
|
||||||
{
|
|
||||||
$certValues[] = "$prefix$certValue";
|
$certValues[] = "$prefix$certValue";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -306,19 +287,16 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
private function _getLastChangeFor($file, $startrev)
|
private function _getLastChangeFor($file, $startrev)
|
||||||
{
|
{
|
||||||
$out = $this->stdio->exec(array(
|
$out = $this->stdio->exec(array(
|
||||||
"get_content_changed", $startrev, $file
|
'get_content_changed', $startrev, $file
|
||||||
));
|
));
|
||||||
|
|
||||||
$stanzas = self::_parseBasicIO($out);
|
$stanzas = self::_parseBasicIO($out);
|
||||||
|
|
||||||
// FIXME: we only care about the first returned content mark
|
// FIXME: we only care about the first returned content mark
|
||||||
// everything else seem to be very, very rare cases
|
// everything else seem to be very, very rare cases
|
||||||
foreach ($stanzas as $stanza)
|
foreach ($stanzas as $stanza) {
|
||||||
{
|
foreach ($stanza as $stanzaline) {
|
||||||
foreach ($stanza as $stanzaline)
|
if ($stanzaline['key'] == 'content_mark') {
|
||||||
{
|
|
||||||
if ($stanzaline['key'] == "content_mark")
|
|
||||||
{
|
|
||||||
return $stanzaline['hash'];
|
return $stanzaline['hash'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -333,7 +311,7 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
{
|
{
|
||||||
$revs = $this->_resolveSelector($commit);
|
$revs = $this->_resolveSelector($commit);
|
||||||
if (count($revs) == 0) return array();
|
if (count($revs) == 0) return array();
|
||||||
return $this->_getUniqueCertValuesFor($revs, "branch", "h:");
|
return $this->_getUniqueCertValuesFor($revs, 'branch', 'h:');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -341,32 +319,26 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
*/
|
*/
|
||||||
public function getTags()
|
public function getTags()
|
||||||
{
|
{
|
||||||
if (isset($this->cache['tags']))
|
if (isset($this->cache['tags'])) {
|
||||||
{
|
|
||||||
return $this->cache['tags'];
|
return $this->cache['tags'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$out = $this->stdio->exec(array("tags"));
|
$out = $this->stdio->exec(array('tags'));
|
||||||
|
|
||||||
$tags = array();
|
$tags = array();
|
||||||
$stanzas = self::_parseBasicIO($out);
|
$stanzas = self::_parseBasicIO($out);
|
||||||
foreach ($stanzas as $stanza)
|
foreach ($stanzas as $stanza) {
|
||||||
{
|
|
||||||
$tagname = null;
|
$tagname = null;
|
||||||
foreach ($stanza as $stanzaline)
|
foreach ($stanza as $stanzaline) {
|
||||||
{
|
|
||||||
// revision comes directly after the tag stanza
|
// revision comes directly after the tag stanza
|
||||||
if ($stanzaline['key'] == "tag")
|
if ($stanzaline['key'] == 'tag') {
|
||||||
{
|
|
||||||
$tagname = $stanzaline['values'][0];
|
$tagname = $stanzaline['values'][0];
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ($stanzaline['key'] == "revision")
|
if ($stanzaline['key'] == 'revision') {
|
||||||
{
|
|
||||||
// FIXME: warn if multiple revisions have
|
// FIXME: warn if multiple revisions have
|
||||||
// equally named tags
|
// equally named tags
|
||||||
if (!array_key_exists("t:$tagname", $tags))
|
if (!array_key_exists("t:$tagname", $tags)) {
|
||||||
{
|
|
||||||
$tags["t:$tagname"] = $tagname;
|
$tags["t:$tagname"] = $tagname;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -385,7 +357,7 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
{
|
{
|
||||||
$revs = $this->_resolveSelector($commit);
|
$revs = $this->_resolveSelector($commit);
|
||||||
if (count($revs) == 0) return array();
|
if (count($revs) == 0) return array();
|
||||||
return $this->_getUniqueCertValuesFor($revs, "tag", "t:");
|
return $this->_getUniqueCertValuesFor($revs, 'tag', 't:');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -394,22 +366,20 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
public function getTree($commit, $folder='/', $branch=null)
|
public function getTree($commit, $folder='/', $branch=null)
|
||||||
{
|
{
|
||||||
$revs = $this->_resolveSelector($commit);
|
$revs = $this->_resolveSelector($commit);
|
||||||
if (count($revs) == 0)
|
if (count($revs) == 0) {
|
||||||
{
|
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
$out = $this->stdio->exec(array(
|
$out = $this->stdio->exec(array(
|
||||||
"get_manifest_of", $revs[0]
|
'get_manifest_of', $revs[0]
|
||||||
));
|
));
|
||||||
|
|
||||||
$files = array();
|
$files = array();
|
||||||
$stanzas = self::_parseBasicIO($out);
|
$stanzas = self::_parseBasicIO($out);
|
||||||
$folder = $folder == '/' || empty($folder) ? '' : $folder.'/';
|
$folder = $folder == '/' || empty($folder) ? '' : $folder.'/';
|
||||||
|
|
||||||
foreach ($stanzas as $stanza)
|
foreach ($stanzas as $stanza) {
|
||||||
{
|
if ($stanza[0]['key'] == 'format_version')
|
||||||
if ($stanza[0]['key'] == "format_version")
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
$path = $stanza[0]['values'][0];
|
$path = $stanza[0]['values'][0];
|
||||||
@ -421,21 +391,19 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
$file['fullpath'] = $path;
|
$file['fullpath'] = $path;
|
||||||
$file['efullpath'] = self::smartEncode($path);
|
$file['efullpath'] = self::smartEncode($path);
|
||||||
|
|
||||||
if ($stanza[0]['key'] == "dir")
|
if ($stanza[0]['key'] == 'dir') {
|
||||||
{
|
$file['type'] = 'tree';
|
||||||
$file['type'] = "tree";
|
|
||||||
$file['size'] = 0;
|
$file['size'] = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$file['type'] = "blob";
|
$file['type'] = 'blob';
|
||||||
$file['hash'] = $stanza[1]['hash'];
|
$file['hash'] = $stanza[1]['hash'];
|
||||||
$file['size'] = strlen($this->getFile((object)$file));
|
$file['size'] = strlen($this->getFile((object)$file));
|
||||||
}
|
}
|
||||||
|
|
||||||
$rev = $this->_getLastChangeFor($file['fullpath'], $revs[0]);
|
$rev = $this->_getLastChangeFor($file['fullpath'], $revs[0]);
|
||||||
if ($rev !== null)
|
if ($rev !== null) {
|
||||||
{
|
|
||||||
$file['rev'] = $rev;
|
$file['rev'] = $rev;
|
||||||
$certs = $this->_getCerts($rev);
|
$certs = $this->_getCerts($rev);
|
||||||
|
|
||||||
@ -482,17 +450,14 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
$scm = IDF_Scm::get($project);
|
$scm = IDF_Scm::get($project);
|
||||||
$branch = $scm->getMainBranch();
|
$branch = $scm->getMainBranch();
|
||||||
|
|
||||||
if (!empty($commit))
|
if (!empty($commit)) {
|
||||||
{
|
|
||||||
$revs = $scm->_resolveSelector($commit);
|
$revs = $scm->_resolveSelector($commit);
|
||||||
if (count($revs) > 0)
|
if (count($revs) > 0) {
|
||||||
{
|
|
||||||
$certs = $scm->_getCerts($revs[0]);
|
$certs = $scm->_getCerts($revs[0]);
|
||||||
// for the very seldom case that a revision
|
// for the very seldom case that a revision
|
||||||
// has no branch certificate
|
// has no branch certificate
|
||||||
if (count($certs['branch']) == 0)
|
if (count($certs['branch']) == 0) {
|
||||||
{
|
$branch = '*';
|
||||||
$branch = "*";
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -502,12 +467,11 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
}
|
}
|
||||||
|
|
||||||
$remote_url = Pluf::f('mtn_remote_url', '');
|
$remote_url = Pluf::f('mtn_remote_url', '');
|
||||||
if (empty($remote_url))
|
if (empty($remote_url)) {
|
||||||
{
|
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
return sprintf($remote_url, $project->shortname)."?".$branch;
|
return sprintf($remote_url, $project->shortname).'?'.$branch;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -527,8 +491,7 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
*/
|
*/
|
||||||
public static function factory($project)
|
public static function factory($project)
|
||||||
{
|
{
|
||||||
if (!array_key_exists($project->shortname, self::$instances))
|
if (!array_key_exists($project->shortname, self::$instances)) {
|
||||||
{
|
|
||||||
self::$instances[$project->shortname] =
|
self::$instances[$project->shortname] =
|
||||||
new IDF_Scm_Monotone($project);
|
new IDF_Scm_Monotone($project);
|
||||||
}
|
}
|
||||||
@ -558,15 +521,14 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
$out = $this->stdio->exec(array(
|
$out = $this->stdio->exec(array(
|
||||||
"get_manifest_of", $revs[0]
|
'get_manifest_of', $revs[0]
|
||||||
));
|
));
|
||||||
|
|
||||||
$files = array();
|
$files = array();
|
||||||
$stanzas = self::_parseBasicIO($out);
|
$stanzas = self::_parseBasicIO($out);
|
||||||
|
|
||||||
foreach ($stanzas as $stanza)
|
foreach ($stanzas as $stanza) {
|
||||||
{
|
if ($stanza[0]['key'] == 'format_version')
|
||||||
if ($stanza[0]['key'] == "format_version")
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
$path = $stanza[0]['values'][0];
|
$path = $stanza[0]['values'][0];
|
||||||
@ -576,15 +538,14 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
$file = array();
|
$file = array();
|
||||||
$file['fullpath'] = $path;
|
$file['fullpath'] = $path;
|
||||||
|
|
||||||
if ($stanza[0]['key'] == "dir")
|
if ($stanza[0]['key'] == "dir") {
|
||||||
{
|
|
||||||
$file['type'] = "tree";
|
$file['type'] = "tree";
|
||||||
$file['hash'] = null;
|
$file['hash'] = null;
|
||||||
$file['size'] = 0;
|
$file['size'] = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$file['type'] = "blob";
|
$file['type'] = 'blob';
|
||||||
$file['hash'] = $stanza[1]['hash'];
|
$file['hash'] = $stanza[1]['hash'];
|
||||||
$file['size'] = strlen($this->getFile((object)$file));
|
$file['size'] = strlen($this->getFile((object)$file));
|
||||||
}
|
}
|
||||||
@ -593,8 +554,7 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
$file['file'] = $pathinfo['basename'];
|
$file['file'] = $pathinfo['basename'];
|
||||||
|
|
||||||
$rev = $this->_getLastChangeFor($file['fullpath'], $revs[0]);
|
$rev = $this->_getLastChangeFor($file['fullpath'], $revs[0]);
|
||||||
if ($rev !== null)
|
if ($rev !== null) {
|
||||||
{
|
|
||||||
$file['rev'] = $rev;
|
$file['rev'] = $rev;
|
||||||
$certs = $this->_getCerts($rev);
|
$certs = $this->_getCerts($rev);
|
||||||
|
|
||||||
@ -619,12 +579,11 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
public function getFile($def, $cmd_only=false)
|
public function getFile($def, $cmd_only=false)
|
||||||
{
|
{
|
||||||
// this won't work with remote databases
|
// this won't work with remote databases
|
||||||
if ($cmd_only)
|
if ($cmd_only) {
|
||||||
{
|
|
||||||
throw new Pluf_Exception_NotImplemented();
|
throw new Pluf_Exception_NotImplemented();
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->stdio->exec(array("get_file", $def->hash));
|
return $this->stdio->exec(array('get_file', $def->hash));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -637,8 +596,7 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
*/
|
*/
|
||||||
private function _getDiff($target, $source = null)
|
private function _getDiff($target, $source = null)
|
||||||
{
|
{
|
||||||
if (empty($source))
|
if (empty($source)) {
|
||||||
{
|
|
||||||
$source = "p:$target";
|
$source = "p:$target";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -647,20 +605,18 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
$targets = $this->_resolveSelector($target);
|
$targets = $this->_resolveSelector($target);
|
||||||
$sources = $this->_resolveSelector($source);
|
$sources = $this->_resolveSelector($source);
|
||||||
|
|
||||||
if (count($targets) == 0 || count($sources) == 0)
|
if (count($targets) == 0 || count($sources) == 0) {
|
||||||
{
|
return '';
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if target contains a root revision, we cannot produce a diff
|
// if target contains a root revision, we cannot produce a diff
|
||||||
if (empty($sources[0]))
|
if (empty($sources[0])) {
|
||||||
{
|
return '';
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->stdio->exec(
|
return $this->stdio->exec(
|
||||||
array("content_diff"),
|
array('content_diff'),
|
||||||
array("r" => array($sources[0], $targets[0]))
|
array('r' => array($sources[0], $targets[0]))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -676,7 +632,7 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
$certs = $this->_getCerts($revs[0]);
|
$certs = $this->_getCerts($revs[0]);
|
||||||
|
|
||||||
// FIXME: this assumes that author, date and changelog are always given
|
// FIXME: this assumes that author, date and changelog are always given
|
||||||
$res['author'] = implode(", ", $certs['author']);
|
$res['author'] = implode(', ', $certs['author']);
|
||||||
|
|
||||||
$dates = array();
|
$dates = array();
|
||||||
foreach ($certs['date'] as $date)
|
foreach ($certs['date'] as $date)
|
||||||
@ -697,9 +653,8 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
*/
|
*/
|
||||||
public function isCommitLarge($commit=null)
|
public function isCommitLarge($commit=null)
|
||||||
{
|
{
|
||||||
if (empty($commit))
|
if (empty($commit)) {
|
||||||
{
|
$commit = 'h:'.$this->getMainBranch();
|
||||||
$commit = "h:"+$this->getMainBranch();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$revs = $this->_resolveSelector($commit);
|
$revs = $this->_resolveSelector($commit);
|
||||||
@ -707,15 +662,14 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
$out = $this->stdio->exec(array(
|
$out = $this->stdio->exec(array(
|
||||||
"get_revision", $revs[0]
|
'get_revision', $revs[0]
|
||||||
));
|
));
|
||||||
|
|
||||||
$newAndPatchedFiles = 0;
|
$newAndPatchedFiles = 0;
|
||||||
$stanzas = self::_parseBasicIO($out);
|
$stanzas = self::_parseBasicIO($out);
|
||||||
|
|
||||||
foreach ($stanzas as $stanza)
|
foreach ($stanzas as $stanza) {
|
||||||
{
|
if ($stanza[0]['key'] == 'patch' || $stanza[0]['key'] == 'add_file')
|
||||||
if ($stanza[0]['key'] == "patch" || $stanza[0]['key'] == "add_file")
|
|
||||||
$newAndPatchedFiles++;
|
$newAndPatchedFiles++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -731,11 +685,9 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
$initialBranches = array();
|
$initialBranches = array();
|
||||||
$logs = array();
|
$logs = array();
|
||||||
|
|
||||||
while (!empty($horizont) && $n > 0)
|
while (!empty($horizont) && $n > 0) {
|
||||||
{
|
if (count($horizont) > 1) {
|
||||||
if (count($horizont) > 1)
|
$out = $this->stdio->exec(array('toposort') + $horizont);
|
||||||
{
|
|
||||||
$out = $this->stdio->exec(array("toposort") + $horizont);
|
|
||||||
$horizont = preg_split("/\n/", $out, -1, PREG_SPLIT_NO_EMPTY);
|
$horizont = preg_split("/\n/", $out, -1, PREG_SPLIT_NO_EMPTY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -743,14 +695,12 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
$certs = $this->_getCerts($rev);
|
$certs = $this->_getCerts($rev);
|
||||||
|
|
||||||
// read in the initial branches we should follow
|
// read in the initial branches we should follow
|
||||||
if (count($initialBranches) == 0)
|
if (count($initialBranches) == 0) {
|
||||||
{
|
|
||||||
$initialBranches = $certs['branch'];
|
$initialBranches = $certs['branch'];
|
||||||
}
|
}
|
||||||
|
|
||||||
// only add it to our log if it is on one of the initial branches
|
// only add it to our log if it is on one of the initial branches
|
||||||
if (count(array_intersect($initialBranches, $certs['branch'])) > 0)
|
if (count(array_intersect($initialBranches, $certs['branch'])) > 0) {
|
||||||
{
|
|
||||||
--$n;
|
--$n;
|
||||||
|
|
||||||
$log = array();
|
$log = array();
|
||||||
@ -771,7 +721,7 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
$logs[] = (object)$log;
|
$logs[] = (object)$log;
|
||||||
}
|
}
|
||||||
|
|
||||||
$out = $this->stdio->exec(array("parents", $rev));
|
$out = $this->stdio->exec(array('parents', $rev));
|
||||||
$horizont += preg_split("/\n/", $out, -1, PREG_SPLIT_NO_EMPTY);
|
$horizont += preg_split("/\n/", $out, -1, PREG_SPLIT_NO_EMPTY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,29 +70,26 @@ class IDF_Scm_Monotone_Stdio
|
|||||||
if (is_resource($this->proc))
|
if (is_resource($this->proc))
|
||||||
$this->stop();
|
$this->stop();
|
||||||
|
|
||||||
$remote_db_access = Pluf::f('mtn_db_access', 'remote') == "remote";
|
$remote_db_access = Pluf::f('mtn_db_access', 'remote') == 'remote';
|
||||||
|
|
||||||
$cmd = Pluf::f('idf_exec_cmd_prefix', '') .
|
$cmd = Pluf::f('idf_exec_cmd_prefix', '') .
|
||||||
Pluf::f('mtn_path', 'mtn') . ' ';
|
Pluf::f('mtn_path', 'mtn') . ' ';
|
||||||
|
|
||||||
$opts = Pluf::f('mtn_opts', array());
|
$opts = Pluf::f('mtn_opts', array());
|
||||||
foreach ($opts as $opt)
|
foreach ($opts as $opt) {
|
||||||
{
|
|
||||||
$cmd .= sprintf('%s ', escapeshellarg($opt));
|
$cmd .= sprintf('%s ', escapeshellarg($opt));
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: we might want to add an option for anonymous / no key
|
// FIXME: we might want to add an option for anonymous / no key
|
||||||
// access, but upstream bug #30237 prevents that for now
|
// access, but upstream bug #30237 prevents that for now
|
||||||
if ($remote_db_access)
|
if ($remote_db_access) {
|
||||||
{
|
|
||||||
$host = sprintf(Pluf::f('mtn_remote_url'), $this->project->shortname);
|
$host = sprintf(Pluf::f('mtn_remote_url'), $this->project->shortname);
|
||||||
$cmd .= sprintf('automate remote_stdio %s', escapeshellarg($host));
|
$cmd .= sprintf('automate remote_stdio %s', escapeshellarg($host));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$repo = sprintf(Pluf::f('mtn_repositories'), $this->project->shortname);
|
$repo = sprintf(Pluf::f('mtn_repositories'), $this->project->shortname);
|
||||||
if (!file_exists($repo))
|
if (!file_exists($repo)) {
|
||||||
{
|
|
||||||
throw new IDF_Scm_Exception(
|
throw new IDF_Scm_Exception(
|
||||||
"repository file '$repo' does not exist"
|
"repository file '$repo' does not exist"
|
||||||
);
|
);
|
||||||
@ -101,19 +98,18 @@ class IDF_Scm_Monotone_Stdio
|
|||||||
}
|
}
|
||||||
|
|
||||||
$descriptors = array(
|
$descriptors = array(
|
||||||
0 => array("pipe", "r"),
|
0 => array('pipe', 'r'),
|
||||||
1 => array("pipe", "w"),
|
1 => array('pipe', 'w'),
|
||||||
2 => array("pipe", "w"),
|
2 => array('pipe', 'w'),
|
||||||
);
|
);
|
||||||
|
|
||||||
$env = array("LANG" => "en_US.UTF-8");
|
$env = array('LANG' => 'en_US.UTF-8');
|
||||||
|
|
||||||
$this->proc = proc_open($cmd, $descriptors, $this->pipes,
|
$this->proc = proc_open($cmd, $descriptors, $this->pipes,
|
||||||
null, $env);
|
null, $env);
|
||||||
|
|
||||||
if (!is_resource($this->proc))
|
if (!is_resource($this->proc)) {
|
||||||
{
|
throw new IDF_Scm_Exception('could not start stdio process');
|
||||||
throw new IDF_Scm_Exception("could not start stdio process");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->_checkVersion();
|
$this->_checkVersion();
|
||||||
@ -154,15 +150,13 @@ class IDF_Scm_Monotone_Stdio
|
|||||||
$read, $write = null, $except = null, 0, 20000
|
$read, $write = null, $except = null, 0, 20000
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($streamsChanged === false)
|
if ($streamsChanged === false) {
|
||||||
{
|
|
||||||
throw new IDF_Scm_Exception(
|
throw new IDF_Scm_Exception(
|
||||||
"Could not select() on read pipe"
|
'Could not select() on read pipe'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($streamsChanged == 0)
|
if ($streamsChanged == 0) {
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,8 +173,7 @@ class IDF_Scm_Monotone_Stdio
|
|||||||
$this->_waitForReadyRead();
|
$this->_waitForReadyRead();
|
||||||
|
|
||||||
$version = fgets($this->pipes[1]);
|
$version = fgets($this->pipes[1]);
|
||||||
if ($version === false)
|
if ($version === false) {
|
||||||
{
|
|
||||||
throw new IDF_Scm_Exception(
|
throw new IDF_Scm_Exception(
|
||||||
"Could not determine stdio version, stderr is:\n".
|
"Could not determine stdio version, stderr is:\n".
|
||||||
$this->_readStderr()
|
$this->_readStderr()
|
||||||
@ -191,8 +184,8 @@ class IDF_Scm_Monotone_Stdio
|
|||||||
$m[1] != self::$SUPPORTED_STDIO_VERSION)
|
$m[1] != self::$SUPPORTED_STDIO_VERSION)
|
||||||
{
|
{
|
||||||
throw new IDF_Scm_Exception(
|
throw new IDF_Scm_Exception(
|
||||||
"stdio format version mismatch, expected '".
|
'stdio format version mismatch, expected "'.
|
||||||
self::$SUPPORTED_STDIO_VERSION."', got '".@$m[1]."'"
|
self::$SUPPORTED_STDIO_VERSION.'", got "'.@$m[1].'"'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,33 +201,28 @@ class IDF_Scm_Monotone_Stdio
|
|||||||
*/
|
*/
|
||||||
private function _write(array $args, array $options = array())
|
private function _write(array $args, array $options = array())
|
||||||
{
|
{
|
||||||
$cmd = "";
|
$cmd = '';
|
||||||
if (count($options) > 0)
|
if (count($options) > 0) {
|
||||||
{
|
$cmd = 'o';
|
||||||
$cmd = "o";
|
foreach ($options as $k => $vals) {
|
||||||
foreach ($options as $k => $vals)
|
|
||||||
{
|
|
||||||
if (!is_array($vals))
|
if (!is_array($vals))
|
||||||
$vals = array($vals);
|
$vals = array($vals);
|
||||||
|
|
||||||
foreach ($vals as $v)
|
foreach ($vals as $v) {
|
||||||
{
|
$cmd .= strlen((string)$k) . ':' . (string)$k;
|
||||||
$cmd .= strlen((string)$k) . ":" . (string)$k;
|
$cmd .= strlen((string)$v) . ':' . (string)$v;
|
||||||
$cmd .= strlen((string)$v) . ":" . (string)$v;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$cmd .= "e ";
|
$cmd .= 'e ';
|
||||||
}
|
}
|
||||||
|
|
||||||
$cmd .= "l";
|
$cmd .= 'l';
|
||||||
foreach ($args as $arg)
|
foreach ($args as $arg) {
|
||||||
{
|
$cmd .= strlen((string)$arg) . ':' . (string)$arg;
|
||||||
$cmd .= strlen((string)$arg) . ":" . (string)$arg;
|
|
||||||
}
|
}
|
||||||
$cmd .= "e\n";
|
$cmd .= "e\n";
|
||||||
|
|
||||||
if (!fwrite($this->pipes[0], $cmd))
|
if (!fwrite($this->pipes[0], $cmd)) {
|
||||||
{
|
|
||||||
throw new IDF_Scm_Exception("could not write '$cmd' to process");
|
throw new IDF_Scm_Exception("could not write '$cmd' to process");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,11 +238,10 @@ class IDF_Scm_Monotone_Stdio
|
|||||||
private function _readStderr()
|
private function _readStderr()
|
||||||
{
|
{
|
||||||
$err = "";
|
$err = "";
|
||||||
while (($line = fgets($this->pipes[2])) !== false)
|
while (($line = fgets($this->pipes[2])) !== false) {
|
||||||
{
|
|
||||||
$err .= $line;
|
$err .= $line;
|
||||||
}
|
}
|
||||||
return empty($err) ? "<empty>" : $err;
|
return empty($err) ? '<empty>' : $err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -273,26 +260,22 @@ class IDF_Scm_Monotone_Stdio
|
|||||||
$output = "";
|
$output = "";
|
||||||
$errcode = 0;
|
$errcode = 0;
|
||||||
|
|
||||||
while (true)
|
while (true) {
|
||||||
{
|
|
||||||
if (!$this->_waitForReadyRead())
|
if (!$this->_waitForReadyRead())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
$data = array(0,"",0);
|
$data = array(0,"",0);
|
||||||
$idx = 0;
|
$idx = 0;
|
||||||
while (true)
|
while (true) {
|
||||||
{
|
|
||||||
$c = fgetc($this->pipes[1]);
|
$c = fgetc($this->pipes[1]);
|
||||||
if ($c === false)
|
if ($c === false) {
|
||||||
{
|
|
||||||
throw new IDF_Scm_Exception(
|
throw new IDF_Scm_Exception(
|
||||||
"No data on stdin, stderr is:\n".
|
"No data on stdin, stderr is:\n".
|
||||||
$this->_readStderr()
|
$this->_readStderr()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($c == ':')
|
if ($c == ':') {
|
||||||
{
|
|
||||||
if ($idx == 2)
|
if ($idx == 2)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -307,24 +290,21 @@ class IDF_Scm_Monotone_Stdio
|
|||||||
}
|
}
|
||||||
|
|
||||||
// sanity
|
// sanity
|
||||||
if ($this->cmdnum != $data[0])
|
if ($this->cmdnum != $data[0]) {
|
||||||
{
|
|
||||||
throw new IDF_Scm_Exception(
|
throw new IDF_Scm_Exception(
|
||||||
"command numbers out of sync; ".
|
'command numbers out of sync; expected '.
|
||||||
"expected {$this->cmdnum}, got {$data[0]}"
|
$this->cmdnum .', got '. $data[0]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$toRead = $data[2];
|
$toRead = $data[2];
|
||||||
$buffer = "";
|
$buffer = "";
|
||||||
while ($toRead > 0)
|
while ($toRead > 0) {
|
||||||
{
|
|
||||||
$buffer .= fread($this->pipes[1], $toRead);
|
$buffer .= fread($this->pipes[1], $toRead);
|
||||||
$toRead = $data[2] - strlen($buffer);
|
$toRead = $data[2] - strlen($buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ($data[1])
|
switch ($data[1]) {
|
||||||
{
|
|
||||||
case 'w':
|
case 'w':
|
||||||
case 'p':
|
case 'p':
|
||||||
case 't':
|
case 't':
|
||||||
@ -340,11 +320,10 @@ class IDF_Scm_Monotone_Stdio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($errcode != 0)
|
if ($errcode != 0) {
|
||||||
{
|
|
||||||
throw new IDF_Scm_Exception(
|
throw new IDF_Scm_Exception(
|
||||||
"command '{$this->lastcmd}' returned error code $errcode: ".
|
"command '{$this->lastcmd}' returned error code $errcode: ".
|
||||||
implode(" ", $this->oob['e'])
|
implode(' ', $this->oob['e'])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,11 +42,11 @@ class IDF_Scm_Monotone_Usher
|
|||||||
*/
|
*/
|
||||||
public static function getServerList($state = null)
|
public static function getServerList($state = null)
|
||||||
{
|
{
|
||||||
$conn = self::_triggerCommand("LIST $state");
|
$conn = self::_triggerCommand('LIST '.$state);
|
||||||
if ($conn == "none")
|
if ($conn == 'none')
|
||||||
return array();
|
return array();
|
||||||
|
|
||||||
return preg_split("/[ ]/", $conn);
|
return preg_split('/[ ]/', $conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -67,19 +67,18 @@ class IDF_Scm_Monotone_Usher
|
|||||||
*/
|
*/
|
||||||
public static function getConnectionList($server = null)
|
public static function getConnectionList($server = null)
|
||||||
{
|
{
|
||||||
$conn = self::_triggerCommand("LISTCONNECTIONS $server");
|
$conn = self::_triggerCommand('LISTCONNECTIONS '.$server);
|
||||||
if ($conn == "none")
|
if ($conn == 'none')
|
||||||
return array();
|
return array();
|
||||||
|
|
||||||
$single_conns = preg_split("/[ ]/", $conn);
|
$single_conns = preg_split('/[ ]/', $conn);
|
||||||
$ret = array();
|
$ret = array();
|
||||||
foreach ($single_conns as $conn)
|
foreach ($single_conns as $conn) {
|
||||||
{
|
preg_match('/\(\w+\)([^:]):(\d+)/', $conn, $matches);
|
||||||
preg_match("/\(\w+\)([^:]):(\d+)/", $conn, $matches);
|
|
||||||
$ret[$matches[1]][] = (object)array(
|
$ret[$matches[1]][] = (object)array(
|
||||||
"server" => $matches[1],
|
'server' => $matches[1],
|
||||||
"address" => $matches[2],
|
'address' => $matches[2],
|
||||||
"port" => $matches[3],
|
'port' => $matches[3],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,7 +95,7 @@ class IDF_Scm_Monotone_Usher
|
|||||||
*/
|
*/
|
||||||
public static function getStatus($server = null)
|
public static function getStatus($server = null)
|
||||||
{
|
{
|
||||||
return self::_triggerCommand("STATUS $server");
|
return self::_triggerCommand('STATUS '.$server);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -110,11 +109,11 @@ class IDF_Scm_Monotone_Usher
|
|||||||
*/
|
*/
|
||||||
public static function matchServer($host, $pattern)
|
public static function matchServer($host, $pattern)
|
||||||
{
|
{
|
||||||
$ret = self::_triggerCommand("MATCH $host $pattern");
|
$ret = self::_triggerCommand('MATCH '.$host.' '.$pattern);
|
||||||
if (preg_match("/^OK: (.+)/", $ret, $m))
|
if (preg_match('/^OK: (.+)/', $ret, $m))
|
||||||
return $m[1];
|
return $m[1];
|
||||||
preg_match("/^ERROR: (.+)/", $ret, $m);
|
preg_match('/^ERROR: (.+)/', $ret, $m);
|
||||||
throw new IDF_Scm_Exception("could not match server: ".$m[1]);
|
throw new IDF_Scm_Exception('could not match server: '.$m[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -143,7 +142,7 @@ class IDF_Scm_Monotone_Usher
|
|||||||
*/
|
*/
|
||||||
public static function startServer($server)
|
public static function startServer($server)
|
||||||
{
|
{
|
||||||
return self::_triggerCommand("START $server");
|
return self::_triggerCommand('START '.$server);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -156,7 +155,7 @@ class IDF_Scm_Monotone_Usher
|
|||||||
*/
|
*/
|
||||||
public static function killServer($server)
|
public static function killServer($server)
|
||||||
{
|
{
|
||||||
return self::_triggerCommand("KILL_NOW $server") == "ok";
|
return self::_triggerCommand('KILL_NOW '.$server) == 'ok';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -166,7 +165,7 @@ class IDF_Scm_Monotone_Usher
|
|||||||
*/
|
*/
|
||||||
public static function shutDown()
|
public static function shutDown()
|
||||||
{
|
{
|
||||||
return self::_triggerCommand("SHUTDOWN") == "ok";
|
return self::_triggerCommand('SHUTDOWN') == 'ok';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -176,7 +175,7 @@ class IDF_Scm_Monotone_Usher
|
|||||||
*/
|
*/
|
||||||
public static function startUp()
|
public static function startUp()
|
||||||
{
|
{
|
||||||
return self::_triggerCommand("STARTUP") == "ok";
|
return self::_triggerCommand('STARTUP') == 'ok';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -186,60 +185,53 @@ class IDF_Scm_Monotone_Usher
|
|||||||
*/
|
*/
|
||||||
public static function reload()
|
public static function reload()
|
||||||
{
|
{
|
||||||
return self::_triggerCommand("RELOAD") == "ok";
|
return self::_triggerCommand('RELOAD') == 'ok';
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function _triggerCommand($cmd)
|
private static function _triggerCommand($cmd)
|
||||||
{
|
{
|
||||||
$uc = Pluf::f('mtn_usher');
|
$uc = Pluf::f('mtn_usher');
|
||||||
if (empty($uc['host']))
|
if (empty($uc['host'])) {
|
||||||
{
|
throw new IDF_Scm_Exception('usher host is empty');
|
||||||
throw new IDF_Scm_Exception("usher host is empty");
|
|
||||||
}
|
}
|
||||||
if (!preg_match('/^\d+$/', $uc['port']) ||
|
if (!preg_match('/^\d+$/', $uc['port']) ||
|
||||||
$uc['port'] == 0)
|
$uc['port'] == 0)
|
||||||
{
|
{
|
||||||
throw new IDF_Scm_Exception("usher port is invalid");
|
throw new IDF_Scm_Exception('usher port is invalid');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($uc['user']))
|
if (empty($uc['user'])) {
|
||||||
{
|
throw new IDF_Scm_Exception('usher user is empty');
|
||||||
throw new IDF_Scm_Exception("usher user is empty");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($uc['pass']))
|
if (empty($uc['pass'])) {
|
||||||
{
|
throw new IDF_Scm_Exception('usher pass is empty');
|
||||||
throw new IDF_Scm_Exception("usher pass is empty");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$sock = @fsockopen($uc['host'], $uc['port'], $errno, $errstr);
|
$sock = @fsockopen($uc['host'], $uc['port'], $errno, $errstr);
|
||||||
if (!$sock)
|
if (!$sock) {
|
||||||
{
|
|
||||||
throw new IDF_Scm_Exception(
|
throw new IDF_Scm_Exception(
|
||||||
"could not connect to usher: $errstr ($errno)"
|
"could not connect to usher: $errstr ($errno)"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fwrite($sock, "USERPASS {$uc['user']} {$uc['pass']}\n");
|
fwrite($sock, 'USERPASS '.$uc['user'].' '.$uc['pass'].'\n');
|
||||||
if (feof($sock))
|
if (feof($sock)) {
|
||||||
{
|
|
||||||
throw new IDF_Scm_Exception(
|
throw new IDF_Scm_Exception(
|
||||||
"usher closed the connection - probably wrong admin ".
|
'usher closed the connection - probably wrong admin '.
|
||||||
"username or password"
|
'username or password'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fwrite($sock, "$cmd\n");
|
fwrite($sock, $cmd.'\n');
|
||||||
$out = "";
|
$out = '';
|
||||||
while (!feof($sock))
|
while (!feof($sock)) {
|
||||||
{
|
|
||||||
$out .= fgets($sock);
|
$out .= fgets($sock);
|
||||||
}
|
}
|
||||||
fclose($sock);
|
fclose($sock);
|
||||||
$out = rtrim($out);
|
$out = rtrim($out);
|
||||||
|
|
||||||
if ($out == "unknown command")
|
if ($out == 'unknown command') {
|
||||||
{
|
|
||||||
throw new IDF_Scm_Exception("unknown command: $cmd");
|
throw new IDF_Scm_Exception("unknown command: $cmd");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,13 +55,11 @@ class IDF_Tests_TestMonotone extends UnitTestCase
|
|||||||
$pipes,
|
$pipes,
|
||||||
$dir);
|
$dir);
|
||||||
|
|
||||||
if (!is_resource($process))
|
if (!is_resource($process)) {
|
||||||
{
|
|
||||||
throw new Exception("could not create process");
|
throw new Exception("could not create process");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($stdin))
|
if (!empty($stdin)) {
|
||||||
{
|
|
||||||
fwrite($pipes[0], $stdin);
|
fwrite($pipes[0], $stdin);
|
||||||
fclose($pipes[0]);
|
fclose($pipes[0]);
|
||||||
}
|
}
|
||||||
@ -70,8 +68,7 @@ class IDF_Tests_TestMonotone extends UnitTestCase
|
|||||||
fclose($pipes[1]);
|
fclose($pipes[1]);
|
||||||
|
|
||||||
$ret = proc_close($process);
|
$ret = proc_close($process);
|
||||||
if ($ret != 0)
|
if ($ret != 0) {
|
||||||
{
|
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
"call ended with a non-zero error code (complete cmdline was: ".
|
"call ended with a non-zero error code (complete cmdline was: ".
|
||||||
implode(" ", $cmdline).")"
|
implode(" ", $cmdline).")"
|
||||||
@ -102,12 +99,9 @@ class IDF_Tests_TestMonotone extends UnitTestCase
|
|||||||
if (is_dir($dirname))
|
if (is_dir($dirname))
|
||||||
$dir_handle=opendir($dirname);
|
$dir_handle=opendir($dirname);
|
||||||
|
|
||||||
while ($file = readdir($dir_handle))
|
while ($file = readdir($dir_handle)) {
|
||||||
{
|
if ($file!="." && $file!="..") {
|
||||||
if ($file!="." && $file!="..")
|
if (!is_dir($dirname."/".$file)) {
|
||||||
{
|
|
||||||
if (!is_dir($dirname."/".$file))
|
|
||||||
{
|
|
||||||
unlink ($dirname."/".$file);
|
unlink ($dirname."/".$file);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -123,8 +117,7 @@ class IDF_Tests_TestMonotone extends UnitTestCase
|
|||||||
|
|
||||||
public function setUp()
|
public function setUp()
|
||||||
{
|
{
|
||||||
if (is_dir($this->tmpdir))
|
if (is_dir($this->tmpdir)) {
|
||||||
{
|
|
||||||
self::deleteRecursive($this->tmpdir);
|
self::deleteRecursive($this->tmpdir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,8 +327,7 @@ class IDF_Views_Admin
|
|||||||
{
|
{
|
||||||
$title = __('Usher management');
|
$title = __('Usher management');
|
||||||
$servers = array();
|
$servers = array();
|
||||||
foreach (IDF_Scm_Monotone_Usher::getServerList() as $server)
|
foreach (IDF_Scm_Monotone_Usher::getServerList() as $server) {
|
||||||
{
|
|
||||||
$servers[] = (object)array(
|
$servers[] = (object)array(
|
||||||
"name" => $server,
|
"name" => $server,
|
||||||
"status" => IDF_Scm_Monotone_Usher::getStatus($server),
|
"status" => IDF_Scm_Monotone_Usher::getStatus($server),
|
||||||
@ -355,21 +354,17 @@ class IDF_Views_Admin
|
|||||||
$title = __('Usher control');
|
$title = __('Usher control');
|
||||||
$action = $match[1];
|
$action = $match[1];
|
||||||
|
|
||||||
if (!empty($action))
|
if (!empty($action)) {
|
||||||
{
|
if (!in_array($action, array('reload', 'shutdown', 'startup'))) {
|
||||||
if (!in_array($action, array("reload", "shutdown", "startup")))
|
|
||||||
{
|
|
||||||
throw new Pluf_HTTP_Error404();
|
throw new Pluf_HTTP_Error404();
|
||||||
}
|
}
|
||||||
|
|
||||||
$msg = null;
|
$msg = null;
|
||||||
if ($action == "reload")
|
if ($action == 'reload') {
|
||||||
{
|
|
||||||
IDF_Scm_Monotone_Usher::reload();
|
IDF_Scm_Monotone_Usher::reload();
|
||||||
$msg = __('Usher configuration has been reloaded');
|
$msg = __('Usher configuration has been reloaded');
|
||||||
}
|
}
|
||||||
else if ($action == "shutdown")
|
else if ($action == 'shutdown') {
|
||||||
{
|
|
||||||
IDF_Scm_Monotone_Usher::shutDown();
|
IDF_Scm_Monotone_Usher::shutDown();
|
||||||
$msg = __('Usher has been shut down');
|
$msg = __('Usher has been shut down');
|
||||||
}
|
}
|
||||||
@ -402,25 +397,21 @@ class IDF_Views_Admin
|
|||||||
public function usherServerControl($request, $match)
|
public function usherServerControl($request, $match)
|
||||||
{
|
{
|
||||||
$server = $match[1];
|
$server = $match[1];
|
||||||
if (!in_array($server, IDF_Scm_Monotone_Usher::getServerList()))
|
if (!in_array($server, IDF_Scm_Monotone_Usher::getServerList())) {
|
||||||
{
|
|
||||||
throw new Pluf_HTTP_Error404();
|
throw new Pluf_HTTP_Error404();
|
||||||
}
|
}
|
||||||
|
|
||||||
$action = $match[2];
|
$action = $match[2];
|
||||||
if (!in_array($action, array("start", "stop", "kill")))
|
if (!in_array($action, array('start', 'stop', 'kill'))) {
|
||||||
{
|
|
||||||
throw new Pluf_HTTP_Error404();
|
throw new Pluf_HTTP_Error404();
|
||||||
}
|
}
|
||||||
|
|
||||||
$msg = null;
|
$msg = null;
|
||||||
if ($action == "start")
|
if ($action == 'start') {
|
||||||
{
|
|
||||||
IDF_Scm_Monotone_Usher::startServer($server);
|
IDF_Scm_Monotone_Usher::startServer($server);
|
||||||
$msg = sprintf(__('The server "%s" has been started'), $server);
|
$msg = sprintf(__('The server "%s" has been started'), $server);
|
||||||
}
|
}
|
||||||
else if ($action == "stop")
|
else if ($action == 'stop') {
|
||||||
{
|
|
||||||
IDF_Scm_Monotone_Usher::stopServer($server);
|
IDF_Scm_Monotone_Usher::stopServer($server);
|
||||||
$msg = sprintf(__('The server "%s" has been stopped'), $server);
|
$msg = sprintf(__('The server "%s" has been stopped'), $server);
|
||||||
}
|
}
|
||||||
@ -443,16 +434,14 @@ class IDF_Views_Admin
|
|||||||
public function usherServerConnections($request, $match)
|
public function usherServerConnections($request, $match)
|
||||||
{
|
{
|
||||||
$server = $match[1];
|
$server = $match[1];
|
||||||
if (!in_array($server, IDF_Scm_Monotone_Usher::getServerList()))
|
if (!in_array($server, IDF_Scm_Monotone_Usher::getServerList())) {
|
||||||
{
|
|
||||||
throw new Pluf_HTTP_Error404();
|
throw new Pluf_HTTP_Error404();
|
||||||
}
|
}
|
||||||
|
|
||||||
$title = sprintf(__('Open connections for "%s"'), $server);
|
$title = sprintf(__('Open connections for "%s"'), $server);
|
||||||
|
|
||||||
$connections = IDF_Scm_Monotone_Usher::getConnectionList($server);
|
$connections = IDF_Scm_Monotone_Usher::getConnectionList($server);
|
||||||
if (count($connections) == 0)
|
if (count($connections) == 0) {
|
||||||
{
|
|
||||||
$request->user->setMessage(sprintf(
|
$request->user->setMessage(sprintf(
|
||||||
__('no connections for server "%s"'), $server
|
__('no connections for server "%s"'), $server
|
||||||
));
|
));
|
||||||
|
Loading…
Reference in New Issue
Block a user