Merge branch 'master' of git://projects.ceondo.com/indefero
This commit is contained in:
commit
f4dbabe8de
@ -3,9 +3,8 @@
|
|||||||
## general
|
## general
|
||||||
|
|
||||||
This version of indefero contains an implementation of the monotone
|
This version of indefero contains an implementation of the monotone
|
||||||
automation interface. It needs at least monotone version 0.47
|
automation interface. It needs at least monotone version 0.99
|
||||||
(interface version 12.0) or newer, but as development continues, its
|
(interface version 13.0) or newer.
|
||||||
likely that this dependency has to be raised.
|
|
||||||
|
|
||||||
To set up a new IDF project with monotone quickly, all you need to do
|
To set up a new IDF project with monotone quickly, all you need to do
|
||||||
is to create a new monotone database with
|
is to create a new monotone database with
|
||||||
|
23
scripts/mtn-post-push
Executable file
23
scripts/mtn-post-push
Executable 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
|
63
scripts/mtnpostpush.php
Normal file
63
scripts/mtnpostpush.php
Normal 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);
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -84,7 +84,7 @@ class IDF_Key extends Pluf_Model
|
|||||||
return array('ssh', $m[2], $m[1]);
|
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();
|
list($type, $keyName, $keyData) = $this->parseContent();
|
||||||
if ($type != 'mtn')
|
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);
|
return sha1($keyName.":".$keyData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,7 +111,7 @@ function IDF_Middleware_ContextPreProcessor($request)
|
|||||||
$request->project);
|
$request->project);
|
||||||
$c = array_merge($c, $request->rights);
|
$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;
|
return $c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
249
src/IDF/Plugin/SyncMonotone.php
Normal file
249
src/IDF/Plugin/SyncMonotone.php
Normal 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)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
71
src/IDF/Plugin/SyncMonotone/monotonerc.tpl
Normal file
71
src/IDF/Plugin/SyncMonotone/monotonerc.tpl
Normal 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
|
@ -22,6 +22,7 @@
|
|||||||
# ***** END LICENSE BLOCK ***** */
|
# ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
require_once(dirname(__FILE__) . "/Monotone/Stdio.php");
|
require_once(dirname(__FILE__) . "/Monotone/Stdio.php");
|
||||||
|
require_once(dirname(__FILE__) . "/Monotone/BasicIO.php");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Monotone scm class
|
* Monotone scm class
|
||||||
@ -121,12 +122,6 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
$branch = "*";
|
$branch = "*";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count($this->_resolveSelector("h:$branch")) == 0) {
|
|
||||||
throw new IDF_Scm_Exception(
|
|
||||||
"Branch $branch is empty"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $branch;
|
return $branch;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,75 +138,6 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
return preg_split("/\n/", $out, -1, PREG_SPLIT_NO_EMPTY);
|
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
|
* Queries the certs for a given revision and returns them in an
|
||||||
* associative array array("branch" => array("branch1", ...), ...)
|
* associative array array("branch" => array("branch1", ...), ...)
|
||||||
@ -226,7 +152,7 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
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 = IDF_Scm_Monotone_BasicIO::parse($out);
|
||||||
$certs = array();
|
$certs = array();
|
||||||
foreach ($stanzas as $stanza) {
|
foreach ($stanzas as $stanza) {
|
||||||
$certname = null;
|
$certname = null;
|
||||||
@ -290,7 +216,7 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
'get_content_changed', $startrev, $file
|
'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
|
// 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
|
||||||
@ -326,7 +252,7 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
$out = $this->stdio->exec(array('tags'));
|
$out = $this->stdio->exec(array('tags'));
|
||||||
|
|
||||||
$tags = array();
|
$tags = array();
|
||||||
$stanzas = self::_parseBasicIO($out);
|
$stanzas = IDF_Scm_Monotone_BasicIO::parse($out);
|
||||||
foreach ($stanzas as $stanza) {
|
foreach ($stanzas as $stanza) {
|
||||||
$tagname = null;
|
$tagname = null;
|
||||||
foreach ($stanza as $stanzaline) {
|
foreach ($stanza as $stanzaline) {
|
||||||
@ -375,7 +301,7 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
));
|
));
|
||||||
|
|
||||||
$files = array();
|
$files = array();
|
||||||
$stanzas = self::_parseBasicIO($out);
|
$stanzas = IDF_Scm_Monotone_BasicIO::parse($out);
|
||||||
$folder = $folder == '/' || empty($folder) ? '' : $folder.'/';
|
$folder = $folder == '/' || empty($folder) ? '' : $folder.'/';
|
||||||
|
|
||||||
foreach ($stanzas as $stanza) {
|
foreach ($stanzas as $stanza) {
|
||||||
@ -525,7 +451,7 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
));
|
));
|
||||||
|
|
||||||
$files = array();
|
$files = array();
|
||||||
$stanzas = self::_parseBasicIO($out);
|
$stanzas = IDF_Scm_Monotone_BasicIO::parse($out);
|
||||||
|
|
||||||
foreach ($stanzas as $stanza) {
|
foreach ($stanzas as $stanza) {
|
||||||
if ($stanza[0]['key'] == 'format_version')
|
if ($stanza[0]['key'] == 'format_version')
|
||||||
@ -666,7 +592,7 @@ class IDF_Scm_Monotone extends IDF_Scm
|
|||||||
));
|
));
|
||||||
|
|
||||||
$newAndPatchedFiles = 0;
|
$newAndPatchedFiles = 0;
|
||||||
$stanzas = self::_parseBasicIO($out);
|
$stanzas = IDF_Scm_Monotone_BasicIO::parse($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')
|
||||||
|
162
src/IDF/Scm/Monotone/BasicIO.php
Normal file
162
src/IDF/Scm/Monotone/BasicIO.php
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -146,8 +146,9 @@ class IDF_Scm_Monotone_Stdio
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
$read = array($this->pipes[1], $this->pipes[2]);
|
$read = array($this->pipes[1], $this->pipes[2]);
|
||||||
|
$write = $except = null;
|
||||||
$streamsChanged = stream_select(
|
$streamsChanged = stream_select(
|
||||||
$read, $write = null, $except = null, 0, 20000
|
$read, $write, $except, 0, 20000
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($streamsChanged === false) {
|
if ($streamsChanged === false) {
|
||||||
|
@ -21,6 +21,8 @@
|
|||||||
#
|
#
|
||||||
# ***** END LICENSE BLOCK ***** */
|
# ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
require_once(dirname(__FILE__) . "/BasicIO.php");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connects with the admininistrative interface of usher,
|
* Connects with the admininistrative interface of usher,
|
||||||
* the monotone proxy. This class contains only static methods because
|
* the monotone proxy. This class contains only static methods because
|
||||||
@ -190,40 +192,60 @@ class IDF_Scm_Monotone_Usher
|
|||||||
|
|
||||||
private static function _triggerCommand($cmd)
|
private static function _triggerCommand($cmd)
|
||||||
{
|
{
|
||||||
$uc = Pluf::f('mtn_usher');
|
$uc = Pluf::f('mtn_usher_conf', false);
|
||||||
if (empty($uc['host'])) {
|
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');
|
throw new IDF_Scm_Exception('usher host is empty');
|
||||||
}
|
}
|
||||||
if (!preg_match('/^\d+$/', $uc['port']) ||
|
if (!preg_match('/^\d+$/', $port))
|
||||||
$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($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($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($host, $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 '.$user.' '.$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 - this should not happen'
|
||||||
'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);
|
||||||
@ -232,7 +254,7 @@ class IDF_Scm_Monotone_Usher
|
|||||||
$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);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $out;
|
return $out;
|
||||||
|
@ -181,7 +181,7 @@ class IDF_Views_Source
|
|||||||
$request_file));
|
$request_file));
|
||||||
return new Pluf_HTTP_Response_Redirect($url, 301);
|
return new Pluf_HTTP_Response_Redirect($url, 301);
|
||||||
}
|
}
|
||||||
if (!$scm->isValidRevision($commit, $request_file)) {
|
if (!$scm->isValidRevision($commit)) {
|
||||||
// Redirect to the first branch
|
// Redirect to the first branch
|
||||||
return new Pluf_HTTP_Response_Redirect($fburl);
|
return new Pluf_HTTP_Response_Redirect($fburl);
|
||||||
}
|
}
|
||||||
|
@ -73,14 +73,16 @@ $cfg['git_write_remote_url'] = 'git@localhost:%s.git';
|
|||||||
$cfg['svn_repositories'] = 'file:///home/svn/repositories/%s';
|
$cfg['svn_repositories'] = 'file:///home/svn/repositories/%s';
|
||||||
$cfg['svn_remote_url'] = 'http://localhost/svn/%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';
|
$cfg['mtn_path'] = 'mtn';
|
||||||
# Additional options for the started monotone process
|
# 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:
|
# 1) One database for everything:
|
||||||
|
#
|
||||||
# Set 'mtn_repositories' below to a fixed database path, such as
|
# Set 'mtn_repositories' below to a fixed database path, such as
|
||||||
# '/home/mtn/repositories/all_projects.mtn'
|
# '/home/mtn/repositories/all_projects.mtn'
|
||||||
#
|
#
|
||||||
@ -94,26 +96,39 @@ $cfg['mtn_opts'] = array('--no-workspace', '--norc');
|
|||||||
# the database
|
# the database
|
||||||
#
|
#
|
||||||
# 2) One database for every project with 'usher':
|
# 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
|
# Again, the '%s' placeholder will be expanded to the project's
|
||||||
# short name. Note that 'mtn_remote_url' is used as internal
|
# short name. Note that 'mtn_remote_url' is used as internal
|
||||||
# URI (to access the data for indefero) as well as external URI
|
# 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'
|
# Example: 'mtn_repositories' is configured to be '/var/monotone/%s'
|
||||||
# (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:
|
|
||||||
#
|
#
|
||||||
# server "project"
|
# - IDF tries to create /var/monotone/<projectname> as root directory
|
||||||
# local "-d" "/home/mtn/repositories/project.mtn" "*"
|
# - 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
|
# therefor /var/monotone MUST be read/writable for the www user and all
|
||||||
# 'project.my-hosting.biz', you can also configure it like this:
|
# 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
|
# Pro: - read and write access can be granted per project
|
||||||
# - no database locking issues
|
# - no database locking issues
|
||||||
@ -143,19 +158,11 @@ $cfg['mtn_remote_url'] = 'mtn://my-host.biz/%s';
|
|||||||
$cfg['mtn_db_access'] = 'remote';
|
$cfg['mtn_db_access'] = 'remote';
|
||||||
#
|
#
|
||||||
# If configured, this allows basic control of a running usher process
|
# If configured, this allows basic control of a running usher process
|
||||||
# via the forge administration
|
# via the forge administration. The variable must point to the full (writable)
|
||||||
#
|
# path of the usher configuration file which gets updated when new projects
|
||||||
# 'host' and 'port' must be set to the specific bits from usher's
|
# are added
|
||||||
# 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',
|
|
||||||
#);
|
|
||||||
#
|
#
|
||||||
|
#$cfg['mtn_usher_conf'] = '/path/to/usher.conf';
|
||||||
|
|
||||||
# Mercurial repositories path
|
# Mercurial repositories path
|
||||||
#$cfg['mercurial_repositories'] = '/home/mercurial/repositories/%s';
|
#$cfg['mercurial_repositories'] = '/home/mercurial/repositories/%s';
|
||||||
|
@ -386,7 +386,7 @@ $ctl[] = array('regex' => '#^/admin/users/(\d+)/$#',
|
|||||||
'model' => 'IDF_Views_Admin',
|
'model' => 'IDF_Views_Admin',
|
||||||
'method' => 'userUpdate');
|
'method' => 'userUpdate');
|
||||||
|
|
||||||
if (Pluf::f("mtn_usher", null) !== null)
|
if (Pluf::f("mtn_usher_conf", null) !== null)
|
||||||
{
|
{
|
||||||
$ctl[] = array('regex' => '#^/admin/usher/$#',
|
$ctl[] = array('regex' => '#^/admin/usher/$#',
|
||||||
'base' => $base,
|
'base' => $base,
|
||||||
|
@ -84,6 +84,13 @@ Pluf_Signal::connect('IDF_Key::preDelete',
|
|||||||
Pluf_Signal::connect('gitpostupdate.php::run',
|
Pluf_Signal::connect('gitpostupdate.php::run',
|
||||||
array('IDF_Plugin_SyncGit', 'entry'));
|
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 --
|
# -- Processing of the webhook queue --
|
||||||
Pluf_Signal::connect('queuecron.php::run',
|
Pluf_Signal::connect('queuecron.php::run',
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
<td>{$server.name}</td>
|
<td>{$server.name}</td>
|
||||||
<td>{$server.status}</td>
|
<td>{$server.status}</td>
|
||||||
<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')}">
|
<a href="{url 'IDF_Views_Admin::usherServerControl', array($server.name, 'stop')}">
|
||||||
{trans 'stop'}</a>
|
{trans 'stop'}</a>
|
||||||
{elseif $server.status == "STOPPED"}
|
{elseif $server.status == "STOPPED"}
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
{foreach $changes as $change}
|
{foreach $changes as $change}
|
||||||
{aurl 'url', 'IDF_Views_Source::commit', array($project.shortname, $change.scm_id)}
|
{aurl 'url', 'IDF_Views_Source::commit', array($project.shortname, $change.scm_id)}
|
||||||
<tr class="log">
|
<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>
|
<td>{issuetext $change.summary, $request}{if $change.fullmessage}<br /><br />{issuetext $change.fullmessage, $request, true, false, true, true, true}{/if}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="extra">
|
<tr class="extra">
|
||||||
|
Loading…
Reference in New Issue
Block a user