Getting information from SVN with PHP

Fabien Potencier
February 03, 2009

Last year, I deployed a new tool to manage symfony plugins. The first goal of this tool was to simplify the process of contributing new plugins. It proved to be very successful, and I had a good feeling that the plugin creation rate was rising since then. And yesterday, I wanted to have hard numbers to back me up.

The question I wanted to answer was quite simple:

How many plugins were created per month before and after the change?

As the symfony project provides free SVN hosting for community plugins, most plugins are actually hosted on the official symfony Subversion repository. And thankfully, the svn command line can give you a lot of information about your project in a very friendly format to parse: XML.

The first step is to get the log file as an XML file:

$ svn log --xml --verbose http://svn.symfony-project.com/plugins > svn_log.xml

The output is similar to something like this:

<?xml version="1.0"?>
<log>
  <logentry revision="1949">
    <author>fabien</author>
    <date>2006-09-05T14:40:20.603942Z</date>
    <paths>
      <path action="A">/plugins/sfGuardPlugin</path>
    </paths>
    <msg>added sfGuardPlugin</msg>
  </logentry>
 
  <!-- many more entries -->
</log>

This XML file contains everything I need: the date of each commit and the paths that were modified, added, or deleted.

Reading an XML file with PHP is insanely easy thanks to simplexml:

$xml = simplexml_load_file(dirname(__FILE__).'/svn_log.xml');

The simplexml_load_file() function reads an XML file and returns an object that can manipulated to access the various information contained in the XML.

Iterating over the logentry and extracting data is also simple enough:

foreach ($xml->logentry as $logentry)
{
  $date = (string) $logentry->date;
 
  foreach ($logentry->paths as $paths)
  {
    foreach ($paths->path as $path)
    {
      $action = $path['action'];
      $content = (string) $path;
    }
  }
}

Each tag is accessible as a property of the object and returns an object representing the XML child elements:

$xml->logentry;

As SimpleXML returns objects, we sometimes need to cast them to string:

$date = (string) $logentry->date;

Getting a tag attribute is as simple as using the object as an array:

$action = $path['action'];

Eventually, getting the content of a tag can be done by casting the object itself to a string:

$content = (string) $path;

Without further ado, the following code parses the XML file and detect plugin creation by checking if the path has been created (A) and if the path is of the form /plugins/XXXPlugin (a symfony plugin name always ends with Plugin).

$numbers = array();
 
$xml = simplexml_load_file(dirname(__FILE__).'/svn_log.xml');
foreach ($xml->logentry as $logentry)
{
  $date = date('d/m/Y', strtotime((string) $logentry->date));
  foreach ($logentry->paths as $paths)
  {
    foreach ($paths->path as $path)
    {
      if ('A' == $path['action'] && preg_match('#^/plugins/.+Plugin$#', (string) $path, $matches))
      {
        if (!isset($numbers[$date]))
        {
          $numbers[$date] = 1;
        }
        else
        {
          ++$numbers[$date];
        }
      }
    }
  }
}

The $numbers array contains the days as keys, and the number of plugins created for this specific particular day as values.

To create a graph, the array can be exported as a simple CSV file to be read by Excel:

foreach ($numbers as $date => $number)
{
  echo "$date,$number\r\n";
}

It outputs the following data:

02/02/2009,1
01/02/2009,1
31/01/2009,1
30/01/2009,2
29/01/2009,1
27/01/2009,3
...

And here is the graph for the data as of today:

I was right. We more than doubled the number of plugin creation per month after the change: from 14 plugins to 30 plugins per month.

http://fabien.potencier.org/article/7/getting-information-from-svn-with-php

Рубрика: php, svn | Комментарии к записи Getting information from SVN with PHP отключены

Make SSH connections with PHP

kvz.io
24 Jul 2007

Not everyone knows about PHP’s capabilities of making SSH connections and executing remote commands, but it can be very useful. I’ve been using it a lot in PHP CLI applications that I run from cronjobs, but initially it was a pain to get it to work. The PHP manual on Secure Shell2 Functions is not very practicle or thorough for that matter, so I would like to share my knowledge in this how to, to make it a little less time consuming setting this up.

In this article I’m going to assume that:

  • You’re running Debian / Ubuntu If not, you will have to substitute the package manager aptitude with whatever your distribution provides
  • You’re running PHP 5 If not, just replace php5 with php4 everywhere
  • You have basic knowledge of PHP & server administration
  • You already have PHP installed

Update

On recent Ubuntu machines, there’s no need to do any compiling anymore:

$ aptitude install libssh2-1-dev libssh2-php

You can now test if PHP recognizes it’s new ssh2 extension by running:

$ php -m |grep ssh2

It should return: ssh2

If the above works for you (you should see also: «Build process completed successfully»), you can skip to: Great! PHP supports SSH — time to code.

Otherwise we need to compile manually, continue reading here.
Prerequisites
Packages

First let’s install the following packages:

$ aptitude install php5-dev php5-cli php-pear build-essential openssl-dev zlib1g-dev

That should set us up alright.
libssh2

We need libssh2 from sourcefourge. We have to compile this, but no worries, this is all you need to do:

$ cd /usr/src \
 && wget http://surfnet.dl.sourceforge.net/sourceforge/libssh2/libssh2-0.14.tar.gz \
 && tar -zxvf libssh2-0.14.tar.gz \
 && cd libssh2-0.14/ \
 && ./configure \
 && make all install

Вот и все! Легко, не так ли?

  • Update: since December 26th 2008, libssh2 has reached version 1.0. Though I have not tested it: it has been reported to work. So you may want to check sf.net and download the latest stable version.

Installation
ssh2.so

Next we need to link libssh & PHP together. There’s a PECL module for this so let’s install using:

$ pecl install -f ssh2

The -f makes sure ssh2 is installed even though there’s not a stable candidate. You could also use the package name: ssh2-beta to overrule this.

Now you need to make sure our new ssh2.so module is loaded by PHP. Edit a php.ini file (I’d recommend a separate one: /etc/php5/conf.d/ssh2.ini). Make sure it reads:

extension=ssh2.so

Great! PHP supports SSH — time to code

You’ve just enabled ssh2 support in PHP. Now how can we make use of this? There are 2 options. SSH supports the:

  • Execute method This tells the server’s operating system to execute something and pipe the output back to your script. (recommended)
  • Shell method This opens an actual shell to the operating system, just as you would normally when logging in with your terminal application. Some routers that don’t have a full POSIX compliant implementation, but run their own application as soon as you login, require this. (advanced)

Метод 1: Execute

Best would be to create functions or even a class for the following code, but this is the basic idea and will definitely get you started:

<?php
if (!function_exists("ssh2_connect")) die("function ssh2_connect doesn't exist");
// log in at server1.example.com on port 22
if(!($con = ssh2_connect("server1.example.com", 22))){
    echo "fail: unable to establish connection\n";
} else {
    // try to authenticate with username root, password secretpassword
    if(!ssh2_auth_password($con, "root", "secretpassword")) {
        echo "fail: unable to authenticate\n";
    } else {
        // allright, we're in!
        echo "okay: logged in...\n";
 
        // execute a command
        if (!($stream = ssh2_exec($con, "ls -al" ))) {
            echo "fail: unable to execute command\n";
        } else {
            // collect returning data from command
            stream_set_blocking($stream, true);
            $data = "";
            while ($buf = fread($stream,4096)) {
                $data .= $buf;
            }
            fclose($stream);
        }
    }
}
?>

Метод 2: Shell

Best would be to create functions or even a class for the following code, but this is the basic idea and will definitely get you started:

<?php
if (!function_exists("ssh2_connect")) die("function ssh2_connect doesn't exist");
// log in at server1.example.com on port 22
if (!($con = ssh2_connect("server1.example.com", 22))) {
    echo "fail: unable to establish connection\n";
} else {
    // try to authenticate with username root, password secretpassword
    if (!ssh2_auth_password($con, "root", "secretpassword")) {
        echo "fail: unable to authenticate\n";
    } else {
        // allright, we're in!
        echo "okay: logged in...\n";
 
        // create a shell
        if (!($shell = ssh2_shell($con, 'vt102', null, 80, 40, SSH2_TERM_UNIT_CHARS))) {
            echo "fail: unable to establish shell\n";
        } else {
            stream_set_blocking($shell, true);
            // send a command
            fwrite($shell, "ls -al\n");
            sleep(1);
 
            // & collect returning data
            $data = "";
            while ($buf = fread($shell,4096)) {
                $data .= $buf;
            }
            fclose($shell);
        }
    }
}
?>

Tips

Sometimes when a server is busy, or a connection is buggy, the buffer may run dry, and the PHP script stops collecting data from a command output (even though the command hasn’t completed yet!). There are a couple of things you could do about that:

<?php
ssh2_exec($con, 'ls -al; echo "__COMMAND_FINISHED__"' );
?>

Now, in the loop where you keep checking for the buffer, just see if the COMMAND_FINISHED line is coming by. Because then you know you have all the data. To avoid infinite loops, just limit the loop with a timeout of 10 seconds or so:

<?php
$time_start = time();
$data       = "";
while (true){
    $data .= fread($stream, 4096);
    if (strpos($data,"__COMMAND_FINISHED__") !== false) {
        echo "okay: command finished\n";
        break;
    }
    if ((time()-$time_start) > 10 ) {
        echo "fail: timeout of 10 seconds has been reached\n";
        break;
    }
}
?>

In the example above, you’d better set streamsetblocking to false.

Can’t get enough?

PHP can send files over ssh!

<?php
ssh2_scp_send($con, "/tmp/source.dat", "/tmp/dest.dat", 0644);
?>

Не работает?

Check the following:

  • Did you follow every step of the prerequisites & installation how to in this article?
  • On the serverside, ‘PasswordAuthentication yes’ must be enabled in the sshd_config. Default is yes on most servers, but in some cases you will have to turn this on yourself by making sure the following line is in place in the file: /etc/ssh/sshd_config: PasswordAuthentication yes

If you’ve made any changes, ssh needs a restart

$ /etc/init.d/ssh restart

Post a comment here if it’s still failing. Don’t forget to paste the error that you’re getting.

make: *** [ssh2.lo] Error 1

If you get the error:

/usr/include/php5/Zend/zend_API.h:361: note: expected char * but argument is of type const unsigned char *
make: *** [ssh2.lo] Error 1

that’s because of PHP 5.3 incompatibility. Try this patch:

$ mkdir -p /usr/src \
 && cd /usr/src \
 && wget http://pecl.php.net/get/ssh2-0.11.0.tgz \
 && tar xvfz ssh2-0.11.0.tgz \
 && cd ssh2-0.11.0 \
 && wget http://remi.fedorapeople.org/ssh2-php53.patch \
 && patch -p0 < ssh2-php53.patch \
 && phpize && ./configure --with-ssh2 \
 && make

make: *** [ssh2fopenwrappers.lo] Error 1

If you get the error:

/tmp/pear/download/ssh2-0.11.0/ssh2_fopen_wrappers.c:49: error: for each function it appears IN.)
make: *** [ssh2_fopen_wrappers.lo] Error 1
ERROR: `make' failed

This is the reported fix (thanks to BuNker).

Альтернативы

There have been some additional developments since the writing of this article. Checkout:
Net_SSH2, PEAR’s SSH wrapper (driver based to support multiple ways of establishing the connection)
SSH2, another wrapper by Jaimie Sirovich
phpseclib, a pure PHP implementation — no additional libraries, binaries, bindings required (against all odds, still pretty fast with mcrypt installed)

http://kvz.io/blog/2007/07/24/make-ssh-connections-with-php/

Рубрика: Linux, php | Комментарии к записи Make SSH connections with PHP отключены

SVN + trac: несколько проектов на одном сервере

Несколько вступительных сток:
Данная тема поднималась тут не раз, но рассматривался вариант создания и настройки только одного репозитория. В моём случае нужно было создать три локальных репозитория на одной машине.
Как ОС была выбрана Ubuntu Server x64 с отключеными иксами…
Итак, приступим:

Задача

Установить на только что инсталлированный Ubuntu последнюю версию Trac, создать репозитории для нескольких проектов и настроить окружение соответственно. Структура проектов должна быть полностью корректной, установка максимально быстрой при минимальном количестве пакетов. Авторизация в репозитории и окружения Trac может быть общей, но позволяющей индивидуальную настройку для каждого проекта. Также, установка должна быть максимально независима от версий.

Дано

Ubuntu 10.10 Server x64
Два пользователя: user1 и user2
Два проекта: Some Project и Another Project
Требуется доступ в Trac и репозиторий по адресам /localProjects и /svn соотвественно

Решение

Пункт 1. Установка базовых дистрибутивов, доступных в пакетах.

Для начала можно установить дистрибутивы, доступные в репозиториях Ubuntu. Установим, предварительно обновив данные о пакетах:

sudo apt-get update
 sudo apt-get install python
 sudo apt-get install apache2
 sudo apt-get install subversion
 sudo apt-get install g++

Пункт 2. Установка trac-related пакетов через easy_install.

Для Python существует утилита, облегчающая установку python-пакетов, называемых также яйцами (они имеют расширение *.egg). Установим её:

wget peak.telecommunity.com/dist/ez_setup.py
 sudo python ./ez_setup.py

И посредством неё установим последние версии Pygments (инструмент для подсветки программного кода на Python), Genshi (механизм шаблонов от создателей trac) и собственно самого trac:

sudo easy_install Pygments
 sudo easy_install Genshi
 sudo easy_install Trac

Пункт 3. Создание репозиториев.

Создадим репозитории для наших проектов и сделаем первые коммиты, содержащие отправные точки для их структур. Все репозитории будут находиться в каталоге /var/svn, полностью доступном для сервера, каждый в своём подкаталоге — такой метод удобен при наличии нескольких проектов и это будет заметно в следующем пункте, на этапе настройки авторизации.

sudo mkdir /var/svn
 sudo mkdir /var/svn/someProject
 sudo mkdir /var/svn/anotherProject
 cd /tmp
 sudo rm -rfR * # удалить все обычные файлы
 sudo rm -rfR .* # удалить все скрытые/системные файлы
 mkdir /tmp/someProject
 mkdir /tmp/someProject/trunk
 mkdir /tmp/someProject/tags
 mkdir /tmp/someProject/branches
 mkdir /tmp/anotherProject
 mkdir /tmp/anotherProject/trunk
 mkdir /tmp/anotherProject/tags
 mkdir /tmp/anotherProject/branches
 sudo svnadmin create /var/svn/someProject
 sudo svn import ./someProject file:///var/svn/someProject \
 -m "Initial import"
 sudo svnadmin create /var/svn/anotherProject
 sudo svn import ./anotherProject file:///var/svn/anotherProject \
 -m "Initinal import"
 sudo chown -R www-data:www-data /var/svn

Пункт 4. Связывание apache и subversion.

Необходимо настроить доступ извне для созданных репозиториев. Для этого нужно установить модуль dav_svn для apache2 и заодно, раз мы работаем с subversion, установим связку subversion c Python, для корректной работы trac с репозиториями:

sudo apt-get install libapache2-svn
 sudo apt-get install python-subversion

Теперь нужно настроить установленный модуль (при установки он автоматически включается для apache, если нет — используйте a2enmod dav_svn по завершению настройки):

sudo vi /etc/apache2/mods-available/dav_svn.conf

Ниже приведено точное содержимое конфигурационного файла. При обращении на путь /svn/… модуль авторизации apache будет обращаться к файлу /etc/apache2/dav_svn.passwd за списком пользователей, а затем давать права на доступ к соответствующему проекту из файла /etc/apache2/dav_svn.authz. Обратите также внимание на использование SVNParentPath вместо SVNPath — таким образом subversion-модуль поймёт, что мы используем мультипроектную структуру и будет обрабатывать путь не как один общий репозиторий, а как несколько внутренних:

<Location /svn>
 DAV svn
 SVNParentPath /var/svn
 AuthType Basic
 AuthName "Subversion Repository"
 AuthUserFile /etc/apache2/dav_svn.passwd
 AuthzSVNAccessFile /etc/apache2/dav_svn.authz
 Require valid-user

Создадим соответствующих пользователей в файлах авторизации. Используйте пароли попроще для проверки и не забудьте их потом поменять:

sudo htpasswd -c /etc/apache2/dav_svn.passwd user1
 sudo htpasswd /etc/apache2/dav_svn.passwd user2

Создадим файл аутентификации:

sudo vi /etc/apache2/dav_svn.authz

В открытым файле опишем права доступа (на чтение — “r” и на запись — “w“) пользователей в соответствующие репозитории:

[/]
 user1=r
 user2=r
 
 [/someProject]
 user1=rw
 user2=r
 
 [/anotherProject]
 user1=r
 user2=rw

Пункт 5. Создание окружений trac.

Создадим каталог, в котором будут находиться окружения для соответствующих проектов.

sudo mkdir /var/trac
 cd /var/trac

Теперь создадим для каждого из них, по очереди, окружение:

sudo trac-admin someProject initenv
 sudo trac-admin anotherProject initenv

Имена проектов остаются на ваше усмотрение, тип репозиториев — по умолчанию svn (просто нажать Enter), путь к базе общий, по умолчанию (sqlite:db/trac.db, аналогично), пути к репозиториям: /var/svn/someProject и /var/svn/anotherProject соответственно.

Дадим права apache пользоваться этим каталогом.

sudo chown -R www-data:www-data /var/trac

Пункт 6. Связывание apache и trac.

Есть несколько вариантов такого связывания, мы остановимся на быстром, но надёжном способе — через mod_python (описания способов на сайте trac). Для этого модуль нужно установить (также, если он не включился после установки, по завершению настройки используйте a2enmod mod_python):

sudo apt-get install libapache2-mod-python

Настроим доступ к окружениям trac:

sudo vim /etc/apache2/sites-available/trac

Эта настройка специфична для использования mod_python (руководство на сайте trac, см. описания, если необходимы другие способы настройки). Обработчиком обращений по адресу /localProjects выступит модуль, он будет рассматривать каталог /var/trac/ как корень нескольких проектов и содаст страницу с их списком (редактируемый шаблон можно найти внутри исходников trac), аналогично принципам SVNParentPath, URI передаётся в код trac. Запросы на вход будут обрабатываться по пользователям из того же passwd файла, из которого берёт их список subversion, а их права на действия в окружениях trac раздаются через trac-admin или в GUI-версии TracAdmin, доступной для аминистраторов окружений (будьте внимательны, пользователи создаваемые через интерфейс также добавляются в этот файл и доступны к использованию для настройки авторизации в subversion через authz-файл (по умолчанию у них нет никаких прав)).

<Location /localProjects>
 SetHandler mod_python
 PythonInterpreter main_interpreter
 PythonHandler trac.web.modpython_frontend
 PythonOption TracEnvParentDir /var/trac
 PythonOption TracUriRoot /localProjects
 
 
 <LocationMatch /localProjects/[^/]+/login>
 AuthType Basic
 AuthName “Local Projects”
 AuthUserFile /etc/apache2/dav_svn.passwd
 Require valid-user

SVN, trac, ubuntu
http://habrahabr.ru/sandbox/22614/

Рубрика: Linux, svn, trac | Комментарии к записи SVN + trac: несколько проектов на одном сервере отключены

Centreon. Установка

Рубрика: all | Комментарии к записи Centreon. Установка отключены

Ethernet Controlled Power Switch

IP-контроль питанием

Рубрика: all | Комментарии к записи Ethernet Controlled Power Switch отключены