Migrating SVN apps to Git and Composer

Just recently I had to move a few remaining legacy CakePHP2.x apps from SVN to Git+Composer.
This serves as a short how-to.

You can also use this post if you are migrating a git app to a composer based git app.

Note: Installing a new CakePHP 2.x app with Git and Composer right away is dead easy and explained quite well in the cookbook. This post is more about migrating existing ones.

SVN + Composer/Git?

They can actually work side-by-side. For a migration process this can be useful, especially when migrating huge apps.
Then doing it piece by piece helps to avoid chaos.

For this little how-to the main repo will stay a SVN one until the very end for this very same reason.

First steps

We have our SVN repo – up to date thanks to svn update.
Download composer.phar in your APP dir. This file should be added to your excludes. Set up a composer.json file in there, as well, and commit it.

CakePHP Core

You might have hard-coded the core files in ROOT/lib/Cake – or used an svn:externals on ROOT/lib.
Either way, remove that and add CakePHP2.x as Composer dependency.

Update index.php and webroot.php

As per documentation, we now need this in those files:

define('CAKE_CORE_INCLUDE_PATH', ROOT . DS . APP_DIR . DS . 'Vendor' . DS . 'cakephp' . DS . 'cakephp' . DS . 'lib');

Resolve global vendors folder

In case you used a global vendors folder it will now not automatically be included anymore.
To quick-fix this, add this to your bootstrap.php (at the beginning probably):

App::build(array('Vendor' => array(ROOT . DS . 'vendors' . DS)));

This will load your App::import()ed classes again from that folder.
Same goes for the plugins folder, by the way.

In a second step you can then start moving those to the APP/Vendor folder – either as Composer dependency or hard-coded.

When all are transferred, you can remove the global vendors folder and also remove the additional App::build() call.

Resolve svn:externals for plugins

Most plugins will probably be hard-coded or included via svn:externals.
Remove the svn:externals and add them as Composer dependency.
Do the same for any plugins in your ROOT/plugins folder. If they are hard-coded there, move them to the APP/Plugin folder.

Almost done

At this point we already have a fully functional application again that loads all dependencies via composer – even though it is still a SVN repo.
All it needs is

cd .../root && svn update
cd app && composer install

If some global vendor libs are harder to migrate or the "svn to git" conversation is troublesome, you can take your time get it up and running again on this level just fine.
At some point you will have finished the migration though, then proceed to the final step.

Move SVN to Git

When all externals have been resolved, the root vendor and plugin folders have been cleaned and their content moved to the APP pendents, we can now start migrating
the application to Git.
For this we drop the usual /trunk/... folder structure and directly copy the APP folder from your SVN repo into the root of your new Git repo.
The ROOT folder itself serves no purpose anymore, and we can have APP as root folder now.
Therefore in root, you should now have the composer.json file.
For an example, see cakephp-sandbox.

As for "copy": You can either directly copy-and-paste your repo (quick and dirty), or use git-svn tools to preserve your changelogs/history.
The latter is the cleaner approach, of course. See this guide on how to do that. Some services like GitHub might also offer a git-svn push access or link useful tools. Those approaches usually need a folder move of the APP folder content to the root level then on top.

Either way, the more you composer and the less you hard-code your libs, the quicker and easier the svn…git transformation will go.

Now updating app + core + plugins + vendors is just:

cd .../root
git pull
composer install

Since the APP folder is now the root folder, no need to further cd deeper.

Sugar

You may add

require_once dirname(__DIR__) . '/Vendor/autoload.php';

at the top of your bootstrap.php file to add autoloading via PSR for new namespaced repos (on top of the still working App::import() and App::uses() methods).

That means, that you can now use namespaces and third party packages via composer quite easily.
Let’s say we want to use the MediaEmbed lib in our project.
We then simply add it to our composer.json:

"require": {
    "dereuromark/media-embed": "dev-master"
}

In our CakePHP classes, e.g. an EmbedHelper, we can now use it right away:

<?php
use \MediaEmbed\MediaEmbed;

App::uses('AppHelper', 'View/Helper');

class EmbedHelper extends AppHelper {
    /**
     * @param string $url Absolute URL of the video/audio.
     * @return string|null HTML snippet or null on failure.
     */
    public function media($url) {
        $MediaEmbed = new MediaEmbed();
        if ($MediaObject = $MediaEmbed->parseUrl($url)) {
            return $MediaObject->getEmbedCode();
        }
    }
}

Isn’t that awesome? You now have the full PHP5.3+ (GitHub) lib variety out there at your disposal. You might want to check out this list of cool libs.

Private repos

With the externals, it was quite easy to pull from your own repo URLs. Composer makes this a little bit more difficult.
To overcome the issue for GitHub private repos, for example, you can use vcs:

"require": {
    ...
    "your-username/cakephp-some-private-plugin" : "*"
},
"repositories" : [{
        "type" : "vcs",
        "url" : "[email protected]:your-username/cakephp-some-private-plugin"
    },
    ...
],

Note that this will prompt once for your username and password and create a token for re-use:

Your GitHub credentials are required to fetch private repository metadata.
The credentials will be swapped for an OAuth token stored in …/Composer/auth.json, your password will not be stored.

Last tips

This tutorial assumes that you are properly using the current master (2.5.3 at this time) version of CakePHP. Always upgrade to the current master – never use anything older.

Make sure you have a current index.php/test.php file as well as a current APP/Console dir including those cake scripts (current as in master/2.5.3 at least).
You can just copy them over from the CakePHP source folder (Cake/Console/Templates/...) if you are unsure.
Especially for console this is quite important. Otherwise Console/cake might not work out of the box with the new composer approach – even though it would if you had up-to-date files.

Also, don’t forget to .gitignore all included dependencies (both Vendor and Plugin ones). And also the ones from SVN, composer.phar etc.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.