Merge branch 'master' of git://projects.ceondo.com/indefero

master
Loïc d'Anterroches 2010-09-01 14:51:33 +02:00
commit f4dbabe8de
17 changed files with 682 additions and 152 deletions

View File

@ -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

View File

@ -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 <http://monotone.ca/docs/Hooks.html#Hooks>.)
#
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

View File

@ -0,0 +1,63 @@
<?php
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
# ***** BEGIN LICENSE BLOCK *****
# This file is part of InDefero, an open source project management application.
# Copyright (C) 2008-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
# 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 ***** */
/**
* This script will send the notifications after a push in your
* repository.
*/
require dirname(__FILE__).'/../src/IDF/conf/path.php';
require 'Pluf.php';
Pluf::start(dirname(__FILE__).'/../src/IDF/conf/idf.php');
Pluf_Dispatcher::loadControllers(Pluf::f('idf_views'));
/**
* [signal]
*
* mtnpostpush.php::run
*
* [sender]
*
* mtnpostpush.php
*
* [description]
*
* This signal allows an application to perform a set of tasks
* after a push to a monotone repository.
*
* [parameters]
*
* array('project' => '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);

View File

@ -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);
}

View File

@ -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;
}

View File

@ -0,0 +1,249 @@
<?php
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
# ***** BEGIN LICENSE BLOCK *****
# This file is part of InDefero, an open source project management application.
# 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
# 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 ***** */
/**
* This classes is a plugin which allows to synchronise access rights
* between indefero and monotone usher setups.
*/
class IDF_Plugin_SyncMonotone
{
/**
* Entry point of the plugin.
*/
static public function entry($signal, &$params)
{
$plug = new IDF_Plugin_SyncMonotone();
switch ($signal) {
case 'IDF_Project::created':
$plug->processMonotoneCreate($params['project']);
break;
case 'mtnpostpush.php::run':
$plug->processSyncTimeline($params);
break;
}
}
/**
* 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
*/
function processMonotoneCreate($project)
{
if ($project->getConf()->getVal('scm') != 'mtn') {
return;
}
$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_conf', false);
if (!$usher_config || !is_writable($usher_config)) {
throw new IDF_Scm_Exception(
'"mtn_usher_conf" does not exist or is not writable.'
);
}
$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)) {
throw new IDF_Scm_Exception(sprintf(
__('The project path %s already exists.'), $projectpath
));
}
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;
$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
));
}
//
// 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 generate_key --confdir=%s %s ""',
escapeshellarg($projectpath),
escapeshellarg($keyname)
);
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
$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
));
}
//
// step 3) write monotonerc for access control
// FIXME: netsync access control is still missing!
//
$monotonerc = file_get_contents(dirname(__FILE__) . "/SyncMonotone/monotonerc.tpl");
$monotonerc = str_replace(
array("%%MTNPOSTPUSH%%", "%%PROJECT%%"),
array($mtnpostpush, $shortname),
$monotonerc
);
$rcfile = $projectpath.'/monotonerc';
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
//
$usher_rc = file_get_contents($usher_config);
$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, $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(
'--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? 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
//
IDF_Scm_Monotone_Usher::reload();
}
/**
* Update the timeline after a push
*
*/
public function processSyncTimeline($params)
{
$pname = $params['project'];
try {
$project = IDF_Project::getOr404($pname);
} catch (Pluf_HTTP_Error404 $e) {
Pluf_Log::event(array(
'IDF_Plugin_SyncMonotone::processSyncTimeline',
'Project not found.',
array($pname, $params)
));
return false; // Project not found
}
Pluf_Log::debug(array(
'IDF_Plugin_SyncMonotone::processSyncTimeline',
'Project found', $pname, $project->id
));
IDF_Scm::syncTimeline($project, true);
Pluf_Log::event(array(
'IDF_Plugin_SyncMonotone::processSyncTimeline',
'sync', array($pname, $project->id)
));
}
}

View File

@ -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

View File

@ -22,6 +22,7 @@
# ***** END LICENSE BLOCK ***** */
require_once(dirname(__FILE__) . "/Monotone/Stdio.php");
require_once(dirname(__FILE__) . "/Monotone/BasicIO.php");
/**
* Monotone scm class
@ -121,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;
}
@ -143,75 +138,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 +152,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 +216,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 +252,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 +301,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 +451,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 +592,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')

View File

@ -0,0 +1,162 @@
<?php
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
# ***** BEGIN LICENSE BLOCK *****
# This file is part of InDefero, an open source project management application.
# 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
# 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 ***** */
/**
* Utility class to parse and compile basic_io stanzas
*
* @author Thomas Keller <me@thomaskeller.biz>
*/
class IDF_Scm_Monotone_BasicIO
{
/**
* 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] == '[') {
unset($stanzaLine['values']);
++$pos; // opening square bracket
$stanzaLine['hash'] = substr($in, $pos, 40);
$pos += 40;
++$pos; // closing square bracket
}
else
{
unset($stanzaLine['hash']);
$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)
{
$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;
}
}

View File

@ -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) {

View File

@ -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,40 +192,60 @@ 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'
);
}
fwrite($sock, $cmd.'\n');
fwrite($sock, $cmd."\n");
$out = '';
while (!feof($sock)) {
$out .= fgets($sock);
@ -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;

View File

@ -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);
}

View File

@ -73,14 +73,16 @@ $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 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/<projectname> as root directory
# - The database is placed in as /var/monotone/<projectname>/database.mtn
# - The server key is put into /var/monotone/<projectname>/keys and
# is named "<projectname>-server@<host>", 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,19 +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
#
#$cfg['mtn_usher'] = array(
# 'host' => 'localhost',
# 'port' => 12345,
# 'user' => 'admin',
# 'pass' => 'admin',
#);
# 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';

View File

@ -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,

View File

@ -49,51 +49,58 @@ 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'));
Pluf_Signal::connect('phppostpush.php::run',
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;

View File

@ -14,7 +14,7 @@
<td>{$server.name}</td>
<td>{$server.status}</td>
<td>
{if preg_match("/ACTIVE|RUNNING|SLEEPING/", $server.status)}
{if preg_match("/ACTIVE|WAITING|RUNNING|SLEEPING/", $server.status)}
<a href="{url 'IDF_Views_Admin::usherServerControl', array($server.name, 'stop')}">
{trans 'stop'}</a>
{elseif $server.status == "STOPPED"}

View File

@ -12,12 +12,12 @@
{foreach $changes as $change}
{aurl 'url', 'IDF_Views_Source::commit', array($project.shortname, $change.scm_id)}
<tr class="log">
<td><a href="{$url}">{$change.creation_dtime|dateago:"wihtout"}</a></td>
<td><a href="{$url}">{$change.creation_dtime|dateago:"without"}</a></td>
<td>{issuetext $change.summary, $request}{if $change.fullmessage}<br /><br />{issuetext $change.fullmessage, $request, true, false, true, true, true}{/if}</td>
</tr>
<tr class="extra">
<td colspan="2">
<div class="helptext right">{trans 'Commit'}&nbsp;<a href="{$url}" class="mono">{$change.scm_id}</a>,
<div class="helptext right">{trans 'Commit'}&nbsp;<a href="{$url}" class="mono">{$change.scm_id}</a>,
{trans 'by'} {showuser $change.get_author(), $request, $change.origauthor}
</div>