Skip navigation

This is not a walk-through, this is a code dump and much like a trash dump you are going to have to do a little digging to find anything useful within. So, say you have gitweb running on your git revision server, and a remote repo somewhere else that you’d like to be able to see which commit the remote repo is sitting at from the gitweb page. This is how:

The first version of this code only handled one repo, this one can be branched into as many as you need.

First off, you need to setup an extremely under privileged user on the host the remote repo sits on. Then you’ll need to install PHP with the ssh2_connect extension on the GitWeb host, along with SSH on both machines. THEN you need to create a set of keys on the remote repo machine and import them to the GitWeb host to use for authentication.

The php query script:

// We are using priv/pub key pair for this, they are protected with .htacess
// !!!! The user for this MUST be highly underpriv'd to avoid the risk of dangerous command execution. We do simple sanitation but the enemy is crafty. !!!!
// 
// note: if you use the url instead of the ip in the connection setup, you will need to update the AllowUsers directive in the sshd config.
// We are restricting access for revuser to the IP of this server only.
class GetHead {

    // the no-priv's user on the host of the remote repo
    protected $sshUser = 'revuser';
    
    // the IP of the remote repo host
    protected $hostIP = '10.16.1.21';
    
    // the SSH port on the remote repo host
    protected $hostPort = 22;
    
    // the path to the local rsa public key
    protected $rsaPub = '/var/www/git/auth/id_rsa.pub';
    
    // the path to the local RSA private key
    protected $rsaPriv = '/var/www/git/auth/id_rsa';

    /**
     * This function queries the remote host via SSH to determine the current HEAD revision of
     * each of the remote repos described within the incoming data.
     *
     * $var $data The JSON encoded data from the ajax request.
     * @return void
     */
    public function GetHead($data) {

        // our return array
        $rDAta = new stdClass();

        // where our json data will go
        $jData = new stdClass();

        // the incoming json deata decoded
        $jData = json_decode($_GET['paths']);

        // assume we are going to succedd
        $rData->success = true;

        // configure the connection using the local keys
        $connection = ssh2_connect($this->hostIP, $this->hostPort, array('hostkey' => 'ssh-rsa'));

        // attempt to authenticate
        if (ssh2_auth_pubkey_file($connection, $this->sshUser, $this->rsaPub, $this->rsaPriv)) {

            // iterate through the local repos and retrieve their HEAD
            foreach ($jData as $name => $local) {

                // the command we are going to exec
                $cmd = 'git --git-dir=' . $local->path . '.git --work-tree=' . $local->path . ' rev-parse HEAD';

                // capture the return stream, blocking is set to ensure that we have data before we try to read from the stream
                $stream = ssh2_exec($connection, $cmd);
                stream_set_blocking($stream, true);

                // $result is the current head revision
                $result = stream_get_contents($stream);

                // close the stream 
                fclose($stream); 

                // make sure we have something and it's not FALSE
                if (!empty($result)) {

                    // the return data
                    $rData->repos->$name->success = true;
                    $rData->repos->$name->head = str_replace(PHP_EOL, '', $result);
                } else {

                    // return the error message
                    $rData->repos->$name->success = false;
                    $rData->repos->$name->error = 'Error retrieving HEAD of remote repository.';
                }
            }
        } else {
            // fail
            $rData->success = false;
            $rData->error = 'SSH Authentication Failed, or missing PHP library.';
        }

        // close the connection
        ssh2_exec($connection, 'exit');
        unset($connection);
        
        // return the data
        return $rData;
    }
}

if ($_SERVER['REQUEST_METHOD'] === 'GET' && !empty($_GET['paths'])) {

    // the incoming data
    $data = filter_input($_GET['paths']);

    // init the class and attempt to get some answers
    $head = new GetHead($data);

    // send the data back as a json obj
    header('Content-Type: application/json');
    echo json_encode($head->getHead());
}else{

    // they are not asking the right questions
    echo 'Go away Dave.';
}
// done
die();

And the Javascript. This script requires jQuery for the AJAX routine, it’s job is to query the above PHP to retrieve the current revision.

// update this object with the repos and their paths on production
var repoObject = {
    'repo.git' : { 
        'repo1' : {
            'path'   : '/var/www/vhosts/remoteRepo1/',
            'branch' : 'master'
        },
        'repo2' : {
            'path'   : '/var/www/vhosts/remoteRepo2/',
            'branch' : 'master'
        }
    }
};
// this is our success function
function querySuccess(data){
    // if we have errors, display them
    if( 'error' in data ){
        $('#query_error').text(data.error);
    }else{
        for(repo in data.repos){
            if(data.repos.hasOwnProperty(repo)) {
                var repoObj = data.repos[repo];
                $('.' + repoObj.head).addClass('currentHead ' + repo);
            }
        }
    }
}
// we run this on document ready for great justice
$(function(){
    var repoTitle = $('#project_name').val();
    // only check rev if we are within a recognized repo
    if (  repoTitle in repoObject) {
        var rData = {
            'paths' : JSON.stringify(repoObject[repoTitle])
            };
        $.ajax({
            dataType: 'json',
            url: '/get_head.php',
            data: rData,
            success: function(data){
                querySuccess(data);
            },
            error: function(){
                $('#query_error').text('There was an error connecting to the server or parsing the response.');
            },
            beforeSend: function(){
                $('#busy_anim').css('display','inline-block');
            },
            complete: function(){
                $('#busy_anim').css('display','none');
            }
        });
    }
});