Merge branch 'mg/status-b'
[gitweb.git] / git-instaweb.sh
index 6a65f255cc63cc7a6d0ae0fc0ce4b65298a40e82..6635fbefdf2a06e556716f91a70181ecf4f3252a 100755 (executable)
@@ -24,6 +24,7 @@ restart        restart the web server
 fqgitdir="$GIT_DIR"
 local="$(git config --bool --get instaweb.local)"
 httpd="$(git config --get instaweb.httpd)"
+root="$(git config --get instaweb.gitwebdir)"
 port=$(git config --get instaweb.port)
 module_path="$(git config --get instaweb.modulepath)"
 
@@ -34,6 +35,9 @@ conf="$GIT_DIR/gitweb/httpd.conf"
 # if installed, it doesn't need further configuration (module_path)
 test -z "$httpd" && httpd='lighttpd -f'
 
+# Default is @@GITWEBDIR@@
+test -z "$root" && root='@@GITWEBDIR@@'
+
 # any untaken local port will do...
 test -z "$port" && port=1234
 
@@ -46,6 +50,12 @@ resolve_full_httpd () {
                        httpd="$httpd -f"
                fi
                ;;
+       *plackup*)
+               # server is started by running via generated gitweb.psgi in $fqgitdir/gitweb
+               full_httpd="$fqgitdir/gitweb/gitweb.psgi"
+               httpd_only="${httpd%% *}" # cut on first space
+               return
+               ;;
        esac
 
        httpd_only="$(echo $httpd | cut -f1 -d' ')"
@@ -57,7 +67,7 @@ resolve_full_httpd () {
                # these days and those are not in most users $PATHs
                # in addition, we may have generated a server script
                # in $fqgitdir/gitweb.
-               for i in /usr/local/sbin /usr/sbin "$fqgitdir/gitweb"
+               for i in /usr/local/sbin /usr/sbin "$root" "$fqgitdir/gitweb"
                do
                        if test -x "$i/$httpd_only"
                        then
@@ -83,8 +93,8 @@ start_httpd () {
 
        # don't quote $full_httpd, there can be arguments to it (-f)
        case "$httpd" in
-       *mongoose*)
-               #The mongoose server doesn't have a daemon mode so we'll have to fork it
+       *mongoose*|*plackup*)
+               #These servers don't have a daemon mode so we'll have to fork it
                $full_httpd "$fqgitdir/gitweb/httpd.conf" &
                #Save the pid before doing anything else (we'll print it later)
                pid=$!
@@ -110,6 +120,20 @@ EOF
 
 stop_httpd () {
        test -f "$fqgitdir/pid" && kill $(cat "$fqgitdir/pid")
+       rm -f "$fqgitdir/pid"
+}
+
+httpd_is_ready () {
+       "$PERL" -MIO::Socket::INET -e "
+local \$| = 1; # turn on autoflush
+exit if (IO::Socket::INET->new('127.0.0.1:$port'));
+print 'Waiting for \'$httpd\' to start ..';
+do {
+       print '.';
+       sleep(1);
+} until (IO::Socket::INET->new('127.0.0.1:$port'));
+print qq! (done)\n!;
+"
 }
 
 while test $# != 0
@@ -159,8 +183,8 @@ done
 mkdir -p "$GIT_DIR/gitweb/tmp"
 GIT_EXEC_PATH="$(git --exec-path)"
 GIT_DIR="$fqgitdir"
-export GIT_EXEC_PATH GIT_DIR
-
+GITWEB_CONFIG="$fqgitdir/gitweb/gitweb_config.perl"
+export GIT_EXEC_PATH GIT_DIR GITWEB_CONFIG
 
 webrick_conf () {
        # generate a standalone server script in $fqgitdir/gitweb.
@@ -192,7 +216,7 @@ EOF
 
        cat >"$conf" <<EOF
 :Port: $port
-:DocumentRoot: "$fqgitdir/gitweb"
+:DocumentRoot: "$root"
 :DirectoryIndex: ["gitweb.cgi"]
 :PidFile: "$fqgitdir/pid"
 EOF
@@ -201,18 +225,18 @@ EOF
 
 lighttpd_conf () {
        cat > "$conf" <<EOF
-server.document-root = "$fqgitdir/gitweb"
+server.document-root = "$root"
 server.port = $port
 server.modules = ( "mod_setenv", "mod_cgi" )
 server.indexfiles = ( "gitweb.cgi" )
 server.pid-file = "$fqgitdir/pid"
-server.errorlog = "$fqgitdir/gitweb/error.log"
+server.errorlog = "$fqgitdir/gitweb/$httpd_only/error.log"
 
 # to enable, add "mod_access", "mod_accesslog" to server.modules
 # variable above and uncomment this
-#accesslog.filename = "$fqgitdir/gitweb/access.log"
+#accesslog.filename = "$fqgitdir/gitweb/$httpd_only/access.log"
 
-setenv.add-environment = ( "PATH" => "/usr/local/bin:/usr/bin:/bin" )
+setenv.add-environment = ( "PATH" => env.PATH, "GITWEB_CONFIG" => env.GITWEB_CONFIG )
 
 cgi.assign = ( ".cgi" => "" )
 
@@ -277,14 +301,15 @@ EOF
 
 apache2_conf () {
        test -z "$module_path" && module_path=/usr/lib/apache2/modules
-       mkdir -p "$GIT_DIR/gitweb/logs"
        bind=
        test x"$local" = xtrue && bind='127.0.0.1:'
        echo 'text/css css' > "$fqgitdir/mime.types"
        cat > "$conf" <<EOF
 ServerName "git-instaweb"
-ServerRoot "$fqgitdir/gitweb"
-DocumentRoot "$fqgitdir/gitweb"
+ServerRoot "$root"
+DocumentRoot "$root"
+ErrorLog "$fqgitdir/gitweb/$httpd_only/error.log"
+CustomLog "$fqgitdir/gitweb/$httpd_only/access.log" combined
 PidFile "$fqgitdir/pid"
 Listen $bind$port
 EOF
@@ -303,13 +328,14 @@ EOF
        # check to see if Dennis Stosberg's mod_perl compatibility patch
        # (<20060621130708.Gcbc6e5c@leonov.stosberg.net>) has been applied
        if test -f "$module_path/mod_perl.so" &&
-          sane_grep 'MOD_PERL' "$GIT_DIR/gitweb/gitweb.cgi" >/dev/null
+          sane_grep 'MOD_PERL' "$root/gitweb.cgi" >/dev/null
        then
                # favor mod_perl if available
                cat >> "$conf" <<EOF
 LoadModule perl_module $module_path/mod_perl.so
 PerlPassEnv GIT_DIR
 PerlPassEnv GIT_EXEC_DIR
+PerlPassEnv GITWEB_CONFIG
 <Location /gitweb.cgi>
        SetHandler perl-script
        PerlResponseHandler ModPerl::Registry
@@ -353,15 +379,15 @@ mongoose_conf() {
 # For detailed description of every option, visit
 # http://code.google.com/p/mongoose/wiki/MongooseManual
 
-root           $fqgitdir/gitweb
+root           $root
 ports          $port
 index_files    gitweb.cgi
 #ssl_cert      $fqgitdir/gitweb/ssl_cert.pem
-error_log      $fqgitdir/gitweb/error.log
-access_log     $fqgitdir/gitweb/access.log
+error_log      $fqgitdir/gitweb/$httpd_only/error.log
+access_log     $fqgitdir/gitweb/$httpd_only/access.log
 
 #cgi setup
-cgi_env                PATH=/usr/local/bin:/usr/bin:/bin,GIT_DIR=$GIT_DIR,GIT_EXEC_PATH=$GIT_EXEC_PATH
+cgi_env                PATH=$PATH,GIT_DIR=$GIT_DIR,GIT_EXEC_PATH=$GIT_EXEC_PATH,GITWEB_CONFIG=$GITWEB_CONFIG
 cgi_interp     $PERL
 cgi_ext                cgi,pl
 
@@ -370,39 +396,165 @@ mime_types       .gz=application/x-gzip,.tar.gz=application/x-tgz,.tgz=application/x-t
 EOF
 }
 
-
-script='
-s#^(my|our) \$projectroot =.*#$1 \$projectroot = "'$(dirname "$fqgitdir")'";#;
-s#(my|our) \$gitbin =.*#$1 \$gitbin = "'$GIT_EXEC_PATH'";#;
-s#(my|our) \$projects_list =.*#$1 \$projects_list = \$projectroot;#;
-s#(my|our) \$git_temp =.*#$1 \$git_temp = "'$fqgitdir/gitweb/tmp'";#;'
-
-gitweb_cgi () {
-       cat > "$1.tmp" <<\EOFGITWEB
-@@GITWEB_CGI@@
-EOFGITWEB
-       # Use the configured full path to perl to match the generated
-       # scripts' 'hashpling' line
-       "$PERL" -p -e "$script" "$1.tmp"  > "$1"
-       chmod +x "$1"
-       rm -f "$1.tmp"
+plackup_conf () {
+       # generate a standalone 'plackup' server script in $fqgitdir/gitweb
+       # with embedded configuration; it does not use "$conf" file
+       cat > "$fqgitdir/gitweb/gitweb.psgi" <<EOF
+#!$PERL
+
+# gitweb - simple web interface to track changes in git repositories
+#          PSGI wrapper and server starter (see http://plackperl.org)
+
+use strict;
+
+use IO::Handle;
+use Plack::MIME;
+use Plack::Builder;
+use Plack::App::WrapCGI;
+use CGI::Emulate::PSGI 0.07; # minimum version required to work with gitweb
+
+# mimetype mapping (from lighttpd_conf)
+Plack::MIME->add_type(
+       ".pdf"          =>      "application/pdf",
+       ".sig"          =>      "application/pgp-signature",
+       ".spl"          =>      "application/futuresplash",
+       ".class"        =>      "application/octet-stream",
+       ".ps"           =>      "application/postscript",
+       ".torrent"      =>      "application/x-bittorrent",
+       ".dvi"          =>      "application/x-dvi",
+       ".gz"           =>      "application/x-gzip",
+       ".pac"          =>      "application/x-ns-proxy-autoconfig",
+       ".swf"          =>      "application/x-shockwave-flash",
+       ".tar.gz"       =>      "application/x-tgz",
+       ".tgz"          =>      "application/x-tgz",
+       ".tar"          =>      "application/x-tar",
+       ".zip"          =>      "application/zip",
+       ".mp3"          =>      "audio/mpeg",
+       ".m3u"          =>      "audio/x-mpegurl",
+       ".wma"          =>      "audio/x-ms-wma",
+       ".wax"          =>      "audio/x-ms-wax",
+       ".ogg"          =>      "application/ogg",
+       ".wav"          =>      "audio/x-wav",
+       ".gif"          =>      "image/gif",
+       ".jpg"          =>      "image/jpeg",
+       ".jpeg"         =>      "image/jpeg",
+       ".png"          =>      "image/png",
+       ".xbm"          =>      "image/x-xbitmap",
+       ".xpm"          =>      "image/x-xpixmap",
+       ".xwd"          =>      "image/x-xwindowdump",
+       ".css"          =>      "text/css",
+       ".html"         =>      "text/html",
+       ".htm"          =>      "text/html",
+       ".js"           =>      "text/javascript",
+       ".asc"          =>      "text/plain",
+       ".c"            =>      "text/plain",
+       ".cpp"          =>      "text/plain",
+       ".log"          =>      "text/plain",
+       ".conf"         =>      "text/plain",
+       ".text"         =>      "text/plain",
+       ".txt"          =>      "text/plain",
+       ".dtd"          =>      "text/xml",
+       ".xml"          =>      "text/xml",
+       ".mpeg"         =>      "video/mpeg",
+       ".mpg"          =>      "video/mpeg",
+       ".mov"          =>      "video/quicktime",
+       ".qt"           =>      "video/quicktime",
+       ".avi"          =>      "video/x-msvideo",
+       ".asf"          =>      "video/x-ms-asf",
+       ".asx"          =>      "video/x-ms-asf",
+       ".wmv"          =>      "video/x-ms-wmv",
+       ".bz2"          =>      "application/x-bzip",
+       ".tbz"          =>      "application/x-bzip-compressed-tar",
+       ".tar.bz2"      =>      "application/x-bzip-compressed-tar",
+       ""              =>      "text/plain"
+);
+
+my \$app = builder {
+       # to be able to override \$SIG{__WARN__} to log build time warnings
+       use CGI::Carp; # it sets \$SIG{__WARN__} itself
+
+       my \$logdir = "$fqgitdir/gitweb/$httpd_only";
+       open my \$access_log_fh, '>>', "\$logdir/access.log"
+               or die "Couldn't open access log '\$logdir/access.log': \$!";
+       open my \$error_log_fh,  '>>', "\$logdir/error.log"
+               or die "Couldn't open error log '\$logdir/error.log': \$!";
+
+       \$access_log_fh->autoflush(1);
+       \$error_log_fh->autoflush(1);
+
+       # redirect build time warnings to error.log
+       \$SIG{'__WARN__'} = sub {
+               my \$msg = shift;
+               # timestamp warning like in CGI::Carp::warn
+               my \$stamp = CGI::Carp::stamp();
+               \$msg =~ s/^/\$stamp/gm;
+               print \$error_log_fh \$msg;
+       };
+
+       # write errors to error.log, access to access.log
+       enable 'AccessLog',
+               format => "combined",
+               logger => sub { print \$access_log_fh @_; };
+       enable sub {
+               my \$app = shift;
+               sub {
+                       my \$env = shift;
+                       \$env->{'psgi.errors'} = \$error_log_fh;
+                       \$app->(\$env);
+               }
+       };
+       # gitweb currently doesn't work with $SIG{CHLD} set to 'IGNORE',
+       # because it uses 'close $fd or die...' on piped filehandle $fh
+       # (which causes the parent process to wait for child to finish).
+       enable_if { \$SIG{'CHLD'} eq 'IGNORE' } sub {
+               my \$app = shift;
+               sub {
+                       my \$env = shift;
+                       local \$SIG{'CHLD'} = 'DEFAULT';
+                       local \$SIG{'CLD'}  = 'DEFAULT';
+                       \$app->(\$env);
+               }
+       };
+       # serve static files, i.e. stylesheet, images, script
+       enable 'Static',
+               path => sub { m!\.(js|css|png)\$! && s!^/gitweb/!! },
+               root => "$root/",
+               encoding => 'utf-8'; # encoding for 'text/plain' files
+       # convert CGI application to PSGI app
+       Plack::App::WrapCGI->new(script => "$root/gitweb.cgi")->to_app;
+};
+
+# make it runnable as standalone app,
+# like it would be run via 'plackup' utility
+if (__FILE__ eq \$0) {
+       require Plack::Runner;
+
+       my \$runner = Plack::Runner->new();
+       \$runner->parse_options(qw(--env deployment --port $port),
+                              "$local" ? qw(--host 127.0.0.1) : ());
+       \$runner->run(\$app);
 }
+__END__
+EOF
 
-gitweb_css () {
-       cat > "$1" <<\EOFGITWEB
-@@GITWEB_CSS@@
-EOFGITWEB
+       chmod a+x "$fqgitdir/gitweb/gitweb.psgi"
+       # configuration is embedded in server script file, gitweb.psgi
+       rm -f "$conf"
 }
 
-gitweb_js () {
-       cat > "$1" <<\EOFGITWEB
-@@GITWEB_JS@@
-EOFGITWEB
+gitweb_conf() {
+       cat > "$fqgitdir/gitweb/gitweb_config.perl" <<EOF
+#!/usr/bin/perl
+our \$projectroot = "$(dirname "$fqgitdir")";
+our \$git_temp = "$fqgitdir/gitweb/tmp";
+our \$projects_list = \$projectroot;
+EOF
 }
 
-gitweb_cgi "$GIT_DIR/gitweb/gitweb.cgi"
-gitweb_css "$GIT_DIR/gitweb/gitweb.css"
-gitweb_js  "$GIT_DIR/gitweb/gitweb.js"
+gitweb_conf
+
+resolve_full_httpd
+mkdir -p "$fqgitdir/gitweb/$httpd_only"
 
 case "$httpd" in
 *lighttpd*)
@@ -417,6 +569,9 @@ webrick)
 *mongoose*)
        mongoose_conf
        ;;
+*plackup*)
+       plackup_conf
+       ;;
 *)
        echo "Unknown httpd specified: $httpd"
        exit 1
@@ -427,7 +582,7 @@ start_httpd
 url=http://127.0.0.1:$port
 
 if test -n "$browser"; then
-       git web--browse -b "$browser" $url || echo $url
+       httpd_is_ready && git web--browse -b "$browser" $url || echo $url
 else
-       git web--browse -c "instaweb.browser" $url || echo $url
+       httpd_is_ready && git web--browse -c "instaweb.browser" $url || echo $url
 fi