Migrate repositories from Gitolite to GitLab

Written in php, I just stumbled upon this tweet.

Visit the gist for an up to date version. The code is:

#!/usr/bin/php -qC
<?php

/******************************************************************************
 *  
 *  @file           gitolab.php
 *  @author         Benoit Zohar
 *  @link           http://benoitzohar.fr/
 *  @last-edited    2015-01-09
 *  @description    Migrate projects from Gitolite to GitLab
 *  @requisite      cURL library for PHP
 *  @usage          1. Change the value of the private variables of the GitoLab class
 *                  2. Launch "php gitolab.php" to start the process
 *                  3. Enter Y or N to import each pre-specified project
 *
 ******************************************************************************/

error_reporting(E_ALL ^ E_NOTICE);

class GitoLab {

    //Gitolite prefix url (you'll need to have you ssh key configured to make this work)
    private $gitolite_url = 'ssh://gitolite@gexample.com/';
    
    //Gitlab API url prefix
    private $gitlab_url = 'https://gitlab.example.com/';
    //Gitlab ssh access prefoex
    private $gitlab_ssh = 'git@gitlab.example.com';
    //Gitlab private token (see Gitlab documentation)
    private $gitlab_private_token = '1234567890AZERTY';

    private $projects = array(
        //there are two ways of defining the repositories you want to import:
        //first: use the very nice long keys of an associative array:
        array(
            //Name of the project in gitolite (name of the repository)
            'liteProjectName' => 'example',
            //Complete name of the project in GitLab
            'labProjectName' => 'ExampleLab',
            //Repository name of the project in GitLab
            'labProjectPath' => 'exampleLab',
            //Namespace in GitLab (must already exist !)
            'labNamespace' => 'NameSpaceEx',
            //Namespace ID in GitLab (must already exist !) use the _ID_ (check GET http://example.com/api/v3/groups to get it)
            'labNamespaceId' => '10',
            //[Optionnal] : description of the project in gitlab
            'labDescription' => 'DescriptionExample'
        ),
        //second: use the quick'n'dirty "no-key" of a standard array: (the keys are in the same order.)
        array(
            'example','ExampleLab','exampleLab','NameSpaceEx','DescriptionExample'
        )
    );

    //Root of temporary directory used to clone repositories
    private $root_dir = '';  
    
    
    public function __construct() {

        //init variables
        if (empty($this->root_dir)) {
            //if the temporary directory is not set: use the current directory of the script
            $this->root_dir = getcwd();
        }

        //start Migration process
        try {
            $this->checkRights();
            $this->startMigration();
        } 
        catch(Exception $e) { 
            $this->cleanTemporaryDirectory();
            die("An error occured : ".$e->getMessage()."\n\n"); 
        }
        $this->cleanTemporaryDirectory();
        echo "\n\n";
    }

    private function checkRights() {

        echo "Checking rights for directory ".$this->root_dir." ...\n";

        if (!is_dir($this->root_dir)) throw new Exception("'".$this->root_dir."' is not a directory");
        if (!is_writable($this->root_dir)) throw new Exception("'".$this->root_dir."' is not a writable");
    }

    private function startMigration() {
        echo "Starting migration ... \n";
        $autoyes = false; //set to true to automatically accept every repository
        foreach($this->projects as $project) {
            if (!$autoyes) $r = readline("Migrate project ".($project['liteProjectName'] ? $project['liteProjectName'] : $project[0])." (Y/N) ? ");

            //set to auto true if the user uses YYY once.
            if ($r === 'YYY') $autoyes = true;

            if ($autoyes || empty($r) || strtolower($r) == 'y') {
                if (array_key_exists('liteProjectName', $project)) {
                    $this->migrateProject(
                        $project['liteProjectName'],
                        $project['labProjectName'],
                        $project['labProjectPath'],
                        $project['labNamespace'],
                        $project['labNamespaceId'],
                        $project['labDescription']
                    );
                } else if (!empty($project[0])) {
                    $this->migrateProject(
                        $project[0],
                        $project[1],
                        $project[2],
                        $project[3],
                        $project[4],
                        $project[5]
                    );
                }
            }
        }

    }

    private function migrateProject($liteProjectName,$labProjectName,$labProjectPath,$labNamespace,$labNamespaceId,$labDescription = '') {
        echo "Migrating project ".$liteProjectName." ...\n";
        
        //create the projet on gitlab
        $url = $this->gitlab_url.'api/v3/projects';
        $data = array(
            'name' => $labProjectName,
            'path' => $labProjectPath,
            'namespace_id' => $labNamespaceId
        );
        if (!empty($labDescription)) {
            $data['description'] = $labDescription;
        }

        $ch = curl_init($url); 
        curl_setopt($ch, CURLOPT_HTTPHEADER, array("PRIVATE-TOKEN: ".$this->gitlab_private_token)); 
        curl_setopt($ch, CURLOPT_HEADER, 0); 
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); 
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); 
        curl_setopt($ch, CURLOPT_POST, 1); 
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data); 
        $head = curl_exec($ch); 
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); 
        curl_close($ch); 
        
        if(!$head) { 
            throw new Exception("Could not curl ".$url);
        } 
        
        if($httpCode != 201) {  //201 for "created" for GitLab API
          throw new Exception("Could not curl ".$url." httpcode=".$httpCode);
        }

        $path = 'gitolab-tmp/'.$labProjectPath;
        
        //clone the project from gitolite
        echo "> 'git clone --mirror ".$this->gitolite_url.$liteProjectName.".git \"".$path."\"'\n";
        exec('git clone --mirror '.$this->gitolite_url.$liteProjectName.'.git "'.$path.'"');

        //move inside temporary path
        chdir($path);

        //clean gitolite remote
        echo "> 'git remote rm origin'\n";
        exec('git remote rm origin');

        //add gitlab remote
        echo "> 'git remote add origin ".$this->gitlab_ssh.":".$labNamespace."/".$labProjectPath.".git'\n";
        exec('git remote add origin '.$this->gitlab_ssh.':'.$labNamespace.'/'.$labProjectPath.'.git');
        
        //push all branches to the new remote
        echo "> 'git push --all'\n";
        exec("git push --all");
        
        //push all tags to the new remote
        echo "> 'git push --tags'\n";
        exec("git push --tags");

        //go back to original dir
        chdir($this->root_dir);
    }
    
    private function cleanTemporaryDirectory() {
        echo "Cleaning temporary directory ...\n";
        $this->rrmdir($this->root_dir."/gitolab-tmp");
    }

    //recursively remove directory
    private function rrmdir($dir) { 
       if (is_dir($dir)) { 
        $objects = scandir($dir); 
        foreach ($objects as $object) { 
          if ($object != "." && $object != "..") { 
            if (filetype($dir."/".$object) == "dir") $this->rrmdir($dir."/".$object); else unlink($dir."/".$object); 
          } 
        } 
        reset($objects); 
        rmdir($dir); 
       } 
    }
}

new GitoLab(); // launch migration