HOWTO setup Trac with Mercurial and Nginx

Development September 7th, 2009

“A goal is a dream with a deadline.” — Napleon Hill

Besides the configuration for Mercurial the preferred DVCS, A bug-tracking and project management tool is required to manage the timeline. Trac is my all-time favorite and it supports the Mercurial via TracMercurial plugin.

Before we move on, we need to rebuild the python-2.6 to get some missing modules with C extensions back:

# Install the following development package to build python modules
sudo yum install bzip2-devel readline-devel gdbm-devel bsddb-devel ncurses-devel db4-devel sqlite-devel
# Rebuild the Python-2.6
./configure –prefix=/opt/python
make && sudo make install

Install Trac and MercurialTrac

sudo /opt/python/bin/easy_install http://svn.edgewall.org/repos/trac/tags/trac-0.11
PATH=/opt/python/bin:$PATH sudo trac-admin /var/trac/bloggo/ initenv

When repository type is prompted, use hg instead.

Then we install the MercurialTrac plugin globally, so it can be used later for other projects:

svn co http://svn.edgewall.com/repos/trac/sandbox/mercurial-plugin-0.11
cd mercurial-plugin-0.11
python setup.py bdist_egg
sudo /opt/python/bin/easy_install dist/TracMercurial-0.11.0.7-py2.6.egg

Now it is time to run Trac in TracStandalone mode to verify the configuration in Trac side.

Configure Nginx to and FastCGI
The Trac instance is loaded via spawn-fcgi as other web applications using this script. The PATH is overrided to use python-2.6, and TRAC_ENV_PARENT_DIR is exported explicitly. I doubt this may contaminate other Trac instances, I will fix it later if so.

The multiple Trac instances are located in the /projects/ path, and we need HTTP Basic Auth to protect the entry point.

location ~ /projects/[^/]+/login {
auth_basic “Trac”;
auth_basic_user_file “/var/trac/devpasswd”;

fastcgi_split_path_info ^(/projects)(/.*)$;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param AUTH_USER $remote_user;
fastcgi_param REMOTE_USER $remote_user;
include fastcgi_params;
fastcgi_pass 127.0.0.1:9004;
}

location /projects/ {
fastcgi_split_path_info ^(/projects)(/.*)$;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param AUTH_USER $remote_user;
fastcgi_param REMOTE_USER $remote_user;
include fastcgi_params;
fastcgi_pass 127.0.0.1:9004;
}

Any thoughts to refactor the configuration to eliminate the duplication?

HOWTO setup the Mercurial with Nginx in CentOS 4

Development September 5th, 2009

As my VPS provider Advantagecom Networks gracefully acknowledged the life-time promotion I enrolled in one year ago, I decided to settle in this ISP. Which also means that I am stuck to the current configuration: python-2.3.4 in CentOS 4.

Unlike Gentoo, there is no shortcut to upgrade CentOS 4 to CentOS 5 seamlessly. Some adventurous pilots tried and failed miserably, also the customer service disregarded this approach. I wish I could reinstall the OS in SSH session. So we fallback the plan B to update the essential components only.

Update the python

wget http://www.python.org/ftp/python/2.6.2/Python-2.6.2.tar.bz2
tar xvfj Python-2.6.2.tar.bz2
cd Python-2.6.2
./configure –prefix=/opt
make && sudo make install

Update the setuptools for python-2.6
Temporarily override the PATH to use python-2.6:

wget http://pypi.python.org/packages/2.6/s/setuptools/setuptools-0.6c9-py2.6.egg#md5=ca37b1ff16fa2ede6e19383e7b59245a
PATH=/opt/python/bin:$PATH sudo sh build/setuptools-0.6c9-py2.6.egg

By default, easy_install will be installed into /opt/python/bin

Install Mercurial and flup

sudo /opt/python/bin/easy_install mercurial
sudo /opt/python/bin/easy_install flup

Setup the Mercurial repository

# Setup the user privilege
sudo /usr/sbin/groupadd hg
sudo /usr/sbin/useradd -g hg -s /bin/false hg
sudo mkdir /var/hg
sudo chown hg:hg /var/hg
sudo chmod g+w /var/hg
# Add myself to hg group
sudo /usr/sbin/usermod -G hg bookstack
hg init /var/hg/bloggo

Serve Mercurial via flup
Copy the fastcgi script hgwebdir.fcgi from /usr/share/doc/mercurial-1.3.1/contrib/, and configure the hgweb.config:

[paths]
/ = /var/hg/**

[web]
style = monoblue
allow_push = *
push_ssl = false

Spawn the fastcgi script using this script.
Note: the PATH environment variable needs to be overrided by /opt/python/bin to use python-2.6.

Install Nginx
Just follow this HOWTO.

Now we need to setup the Nginx to expose the Mercurial repository in nginx.conf:

# HG
server {
listen 80;
server_name dev.kunxi.org;
root /var/web/$host;
access_log logs/$host.access.log main;
error_log logs/$host.error.log;

location /hg/ {
fastcgi_split_path_info ^(/hg)(/.*)$;
fastcgi_param PATH_INFO $fastcgi_path_info;
include fastcgi_params;
limit_except GET HEAD {
deny all;
}
fastcgi_pass 127.0.0.1:9003;
}
}

The directive limit_except in Nginx seem not to work as expected. If we add auth_basic in limit_except block, Nginx will ask for the credential first, then tries to server the static content in /hg/project_name instead of passing the request to the underlying fastcgi. I encountered the similar problem in MoinMoin setup, so I just deny all the POST operations, and use SSH instead. This is not the perfect solution for sure, but it works fine for personal source depot.

Rewrite WordPress and ZenPhoto for Nginx

Web October 15th, 2008

Nginx also supports URL rewrite, not compatible to Apache’s mod_rewrite, but more intuitive and more powerful imho. The only problem is most applications, WordPress and ZenPhoto for this specific case do include the mod_rewrite code snippet and/or may update the .htaccess for your convenience.

Thanks to the Slicehost community, the port of mod_rewrite rules perfectly covers WordPress and SuperCache. Here are some minor modification to craft for more general usage:

# the blog dir, aka where index.php is
set $blog_dir ”;
# the wordpress dir where all wp-* stays
set $wordpress_dir ‘/wordpress’;
include wordpress.rewrite;

In nginx.conf, define wordpress_dir and blog_dir, these two variables are equivalent to WordPress address (URL) and Blog address (URL) stripped off the host information. Then we can replace the hard-coded /blog path by using $wordpress_dir or $blog_dir:

2d1
<
26c25
< set $supercache_file /blog/wp-content/cache/supercache/$http_host/$1index.html;
---
> set $supercache_file $wordpress_dir/wp-content/cache/supercache/$http_host/$1index.html;
36c35
< rewrite . /blog/index.php last;
---
> rewrite ^(.*)$ $blog_dir/index.php?q=$1 last;

Here is my zenphoto.rewrite, it seems sivel’s more concise. Either of these should work.

HOWTO: Serve virtual host with Nginx

Web September 6th, 2008

As the limited memory budget, and I plan to host multiple website in the VPS, I decided to take a less versatile, but lightweight Apache alternative, the Nginx made by the polar bear.

There is no RPM in the repository I have enlisted, so let’s fallback the old-school way:

# remove the blocking glibc-dummy-centos-4 package, then get the toolchain:
yum remove glibc-dummy-centos-4
yum -y install gcc openssl-devel
# Now it is time to build the nginx:
./configure –prefix=/opt/nginx –with-http_ssl_module –with-http_stub_status_module
make && sudo make install

Then get the PHP with FastCGI support, and the lighttpd-fastcgi for the fastcgi loader.

yum install php-cli php-mysql lighttpd-fastcgi

Here is the nginx.conf that server.

user nobody;
worker_processes 2;
pid logs/nginx.pid;
error_log logs/error.log;

events {
worker_connections 2048;
use epoll;
}

http {
include mime.types;
include fastcgi_params;
default_type application/octet-stream;

log_format main ‘$remote_addr – $remote_user [$time_local] $request ‘
‘”$status” $body_bytes_sent “$http_referer” ‘
‘”$http_user_agent” “$http_x_forwarded_for”‘;

access_log logs/access.log main;
client_header_timeout 3m;
client_body_timeout 3m;
send_timeout 3m;

client_header_buffer_size 1k;
large_client_header_buffers 4 4k;

gzip on;
gzip_min_length 1100;
gzip_buffers 4 8k;
gzip_types text/plain;
gzip_static on;

output_buffers 1 32k;
postpone_output 1460;

sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 75 20;

server {
#REVIEW: how to redirect https? using re?
server_name www.kunxi.org;
rewrite ^(.*) http://kunxi.org$1 permanent;
}

server {
# kunxi’s gallery
listen 80;
server_name gallery.kunxi.org;
root /home/webadmin/$host;
access_log logs/$host.log main;

location / {
root /home/webadmin/$host;
index index.html index.htm index.php;
include zenphoto.rewrite;
}

# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
}

# deny access to .htaccess files.
location ~ /\.ht {
deny all;
}
}

server {
# kunxi’s sites
listen 80;
server_name kunxi.org *.kunxi.org;
root /home/webadmin/$host;

error_page 404 $document_root/404.html;
error_page 500 502 503 504 $document_root/50x.html;
access_log logs/$host.log main;

location / {
root /home/webadmin/$host;
index index.html index.htm index.php;

# the blog dir, aka where index.php is
set $blog_dir ”;
# the wordpress dir where all wp-* stays
set $wordpress_dir ‘/wordpress’;

include wordpress.rewrite;
}

# rewrite the /files/
#
location /files/ {
alias /home/webadmin/static.kunxi.org/;
}

# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
}

# deny access to .htaccess files.
#
location ~ /\.ht {
deny all;
}
}
}

Some highlights of the configuration:
Rewrite www.kunxi.org to kunxi.org, yes, we support no-www!

server {
#REVIEW: how to redirect https? using re?
server_name www.kunxi.org;
rewrite ^(.*) http://kunxi.org$1 permanent;
}

And this wildcards will cover all sub-domains powered by PHP:

server_name kunxi.org *.kunxi.org;
root /home/webadmin/$host;
… ….

Home-brewed nginx and fastcgi init scripts to make it works after the reboot:

chkconfig –add nginx
chkconfig –add fcgi-php
service start nginx
service start fcgi-php

Tips and Traps:
Nginx supports 0 downtime upgrade, so if your nginx.conf is wrong, the server would ignore it and suck up the complain. Make sure stop the nginx service and start nginx during debugging rewrite rules.

fcgi-php seems to have problem to parse localhost, so I use 127.0.0.1 instead.

The rewrite rule for WordPress and ZenPhoto are explained here and here. (TODO).