diff --git a/doc/dev-status.mkd b/doc/dev-status.mkd index 28f0866..c9af92a 100644 --- a/doc/dev-status.mkd +++ b/doc/dev-status.mkd @@ -2,7 +2,6 @@ Not yet done (will be tackled in this order unless someone asks): - * smart http * svnserve (someone is testing it) * mechanism for ADCs using unchecked arguments -- this is not just a matter of writing it; I have to think about *how* it will be done. (AFAIK the @@ -33,3 +32,4 @@ Done: * migration documentation * distro packaging instructions * migration advice for common cases + * smart http diff --git a/doc/g3-master-toc.mm b/doc/g3-master-toc.mm index 3e1b569..9d89535 100755 --- a/doc/g3-master-toc.mm +++ b/doc/g3-master-toc.mm @@ -152,7 +152,6 @@ - diff --git a/doc/http.mkd b/doc/http.mkd new file mode 100644 index 0000000..9263d4c --- /dev/null +++ b/doc/http.mkd @@ -0,0 +1,70 @@ +# how to setup gitolite to use smart http mode + +**Note**: "smart http" refers to the feature that came with git 1.6.6, late +2009 or so. The base documentation for this is `man git-http-backend`. Do +**NOT** read `Documentation/howto/setup-git-server-over-http.txt` and think +that is the same or even relevant -- that is from 2006 and is quite different +(and arguably obsolete). + +## WARNINGS and important notes + + * Please read [authentication versus authorisation][auth] first, and make + sure you understand what is gitolite's responsibility and what isn't. + + * The 'gitolite' command (for example, 'gitolite compile', 'gitolite + query-rc', and so on) *can* be run on the server, but it's not + straightforward. Assuming you installed using the exact same values as in + this document: + + * get a shell by using, say, `su -s /bin/bash - apache` + * run `export HOME=$HOME/gitolite-home` + * run `export PATH=$PATH:$HOME/bin` + + Now you can run `gitolite ` + + * I have tested only on stock Fedora 16; YDMV + + * As before, I have not tried making repos available to both ssh *and* http + mode clients but it ought to work. If you managed it, I'd appreciate a + doc patch describing how you did it. + +## additional requirements + + * requires `GIT_PROJECT_ROOT` (see "man git-http-backend" for what this is) + set explicitly (i.e., it is no longer optional). Please set it to some + place outside apache's `DOCUMENT_ROOT`. + +## assumptions: + + * apache 2.x and git installed. + * httpd runs under the "apache" userid; adjust instructions below if not. + * similarly for "/var/www" and other file names/locations. + +## instructions + +The detailed instructions I used to have in g2 have now been replaced by a +script called `t/smart-http.root-setup`. **Do NOT run this script as is -- it +is actually meant for my testing setup and deletes stuff**. However, it does +provide an excellent (and working!) narration of what you need to do to +install gitolite in smart http mode. + +Make a copy of the script, go through it carefully, (possibly removing lines +that delete files etc.), change values per your system, and only then run it. + +## usage + +Git URLs look like `http://user:password@server/git/reponame.git`. + +The custom commands, like "info", "expand" should be handled as follows. The +command name will come just after the `/git/`, followed by a `?`, followed by +the arguments, with `+` representing a space. Here are some examples: + + # ssh git@server info + curl http://user:password@server/git/info + # ssh git@server info repopatt + curl http://user:password@server/git/info?repopatt + # ssh git@server info repopatt user1 user2 + curl http://user:password@server/git/info?repopatt+user1+user2 + +With a few nice shell aliases, you won't even notice the horrible convolutions +here ;-) See t/smart-http for a couple of useful ones. diff --git a/doc/index.mkd b/doc/index.mkd index a2ec9c1..25e3da1 100644 --- a/doc/index.mkd +++ b/doc/index.mkd @@ -46,7 +46,7 @@ most people see: * Can be installed without root access, assuming git and perl are already installed. * Authentication is most commonly done using sshd, but you can also use - httpd if you prefer (this may require root access). + [http][] if you prefer (this may require root access). ## #contact contact diff --git a/doc/install.mkd b/doc/install.mkd index e5799b0..642c4ac 100644 --- a/doc/install.mkd +++ b/doc/install.mkd @@ -45,8 +45,7 @@ Notes: * any Unix system with a posix compatible "sh". * git version 1.6.6 or later * perl 5.8.8 or later - * openssh (almost any version). Optional if you're using the http backend - (which is still a TODO item!) + * openssh (almost any version). Optional if you're using [smart http][http] * a dedicated Unix userid to be the hosting user, usually "git" but it can be any user, even your own normal one. (If you're using an RPM/DEB the install probably created one called "gitolite"). diff --git a/doc/setup.mkd b/doc/setup.mkd index 2b3921a..075400c 100644 --- a/doc/setup.mkd +++ b/doc/setup.mkd @@ -37,5 +37,7 @@ after the install as well: * to replace a [lost admin key][lost-key]. + * to setup gitolite for http mode (run 'gitolite setup -h' for more info) + When in doubt, run 'gitolite setup' anyway; it doesn't do any harm, though it may take a minute or so if you have more than a few thousand repos! diff --git a/src/gitolite-shell b/src/gitolite-shell index f3cec61..6dcc8ba 100755 --- a/src/gitolite-shell +++ b/src/gitolite-shell @@ -8,6 +8,10 @@ use FindBin; BEGIN { $ENV{GL_BINDIR} = $FindBin::RealBin; } BEGIN { $ENV{GL_LIBDIR} = "$ENV{GL_BINDIR}/lib"; } use lib $ENV{GL_LIBDIR}; + +# set HOME +BEGIN { $ENV{HOME} = $ENV{GITOLITE_HTTP_HOME} if $ENV{GITOLITE_HTTP_HOME}; } + use Gitolite::Rc; use Gitolite::Common; use Gitolite::Conf::Load; @@ -48,7 +52,20 @@ sub in_file { } sub in_http { - _die 'http not yet implemented...'; + http_setup_die_handler(); + + _die "GITOLITE_HTTP_HOME not set" unless $ENV{GITOLITE_HTTP_HOME}; + + _die "fallback to DAV not supported" if $ENV{REQUEST_METHOD} eq 'PROPFIND'; + + # fake out SSH_ORIGINAL_COMMAND and SSH_CONNECTION when called via http, + # so the rest of the code stays the same (except the exec at the end). + http_simulate_ssh_connection(); + + $ENV{REMOTE_USER} ||= $rc{HTTP_ANON_USER}; + @ARGV = ( $ENV{REMOTE_USER} ); + + return 'http'; } sub in_ssh { @@ -104,6 +121,8 @@ sub main { gl_log( "pre_git", $repo, $user, $aa, 'any', "-> $ret" ); } + exec $ENV{GIT_HTTP_BACKEND} if $ENV{REQUEST_URI}; + trigger( 'PRE_GIT', $repo, $user, $aa, 'any', $verb ); my $repodir = "'$rc{GL_REPO_BASE}/$repo.git'"; _system( "git", "shell", "-c", "$verb $repodir" ); @@ -146,3 +165,67 @@ sub sanity { _die "'$repo' ends with a '/'" if $repo =~ m(/$); _die "'$repo' contains '..'" if $repo =~ m(\.\.$); } + +# ---------------------------------------------------------------------- +# helper functions for "in_http" + +sub http_setup_die_handler { + + $SIG{__DIE__} = sub { + my $service = ($ENV{SSH_ORIGINAL_COMMAND} =~ /git-receive-pack/ ? 'git-receive-pack' : 'git-upload-pack'); + my $message = shift; chomp($message); + print STDERR "$message\n"; + + # format the service response, then the message. With initial + # help from Ilari and then a more detailed email from Shawn... + $service = "# service=$service\n"; $message = "ERR $message\n"; + $service = sprintf("%04X", length($service)+4) . "$service"; # no CRLF on this one + $message = sprintf("%04X", length($message)+4) . "$message"; + + http_print_headers(); + print $service; + print "0000"; # flush-pkt, apparently + print $message; + print STDERR $service; + print STDERR $message; + exit 0; # if it's ok for die_webcgi in git.git/http-backend.c, it's ok for me ;-) + } +} + +sub http_simulate_ssh_connection { + # these patterns indicate normal git usage; see "services[]" in + # http-backend.c for how I got that. Also note that "info" is overloaded; + # git uses "info/refs...", while gitolite uses "info" or "info?...". So + # there's a "/" after info in the list below + if ($ENV{PATH_INFO} =~ m(^/(.*)/(HEAD$|info/refs$|objects/|git-(?:upload|receive)-pack$))) { + my $repo = $1; + my $verb = ($ENV{REQUEST_URI} =~ /git-receive-pack/) ? 'git-receive-pack' : 'git-upload-pack'; + $ENV{SSH_ORIGINAL_COMMAND} = "$verb '$repo'"; + } else { + # this is one of our custom commands; could be anything really, + # because of the adc feature + my ($verb) = ($ENV{PATH_INFO} =~ m(^/(\S+))); + my $args = $ENV{QUERY_STRING}; + $args =~ s/\+/ /g; + $ENV{SSH_ORIGINAL_COMMAND} = $verb; + $ENV{SSH_ORIGINAL_COMMAND} .= " $args" if $args; + http_print_headers(); # in preparation for the eventual output! + } + $ENV{SSH_CONNECTION} = "$ENV{REMOTE_ADDR} $ENV{REMOTE_PORT} $ENV{SERVER_ADDR} $ENV{SERVER_PORT}"; +} + +my $http_headers_printed = 0; +sub http_print_headers { + my($code, $text) = @_; + + return if $http_headers_printed++; + $code ||= 200; + $text ||= "OK - gitolite"; + + $|++; + print "Status: $code $text\r\n"; + print "Expires: Fri, 01 Jan 1980 00:00:00 GMT\r\n"; + print "Pragma: no-cache\r\n"; + print "Cache-Control: no-cache, max-age=0, must-revalidate\r\n"; + print "\r\n"; +} diff --git a/src/lib/Gitolite/Setup.pm b/src/lib/Gitolite/Setup.pm index e876a50..1e995c2 100644 --- a/src/lib/Gitolite/Setup.pm +++ b/src/lib/Gitolite/Setup.pm @@ -7,9 +7,11 @@ package Gitolite::Setup; Usage: gitolite setup [