Documentation

Overview

Magallanes is a deployment tool for PHP applications built with PHP itself, it's quite simple to use, manage and extend. It will get your application to a safe harbor. You can instruct Magallanes to deploy your code to all the servers you want (via rsync over ssh), and run tasks for that freshly deployed code. You can also instruct Magallanes to run tasks before the deployment starts (e.g: a vendors install) and after the deployment is done (e.g: clear some caches).



Installation & Upgrade

In order to install and use Magallanes, you will need to meet the following requirements:

  • Shell Access - Magallanes is a command line utility, therefore you will need access to a shell/terminal environment.
  • PHP 5.3 - Yes, it's time to grow up. Make sure that the PHP binary is on your shell path.
The installation is very simple, just download the latest version and install it using it's own command. Also you can use composer and set Magallanes as a vendor on your project.


wget http://download.magephp.com/magallanes.latest.tar.gz -O magallanes.tar.gz
tar xfz magallanes.tar.gz


cd magallanes
bin/mage install --systemWide --installDir=/opt/magallanes

The install command will copy the downloaded version to the configured --installDir. If you want to make it available to all users, then you can perform a --systemWide installation, this will create a symbolic link in /usr/bin/mage to the installation's executable; you have to be root to perform a system wide install.

Also, if you would like to take Magallanes with you, you can also compile into a PHAR your currnet install, with the compile command, which will create a mage.phar file. In order for this to work, you must set the phar.readonly php.ini variable to Off, at least on your cli configuration.

mage compile
./mage.phar version



Upgrade

Just as Install, Upgrading is very, very easy! Just run the upgrade command and Magallanes will auto-upgrade itself.
Also with version you can find out the current installed version.

mage upgrade


mage version




Using composer

If you want to have all your tools defined in your project, now you can configure composer to install Magallanes! It's quite simple, just add the requirement line below. Also mage is declared as a vendor binary, so you can invoke Magallanes using bin/mage. Pretty neat, right?!

  • {
    • "require-dev": {
      • "andres-montanez/magallanes": "~1.0.1"
    • }
  • }

Then we update the vendors:

php composer update andres-montanez/magallanes


bin/mage version




Initiating a new Project

In order to use Magallanes we have to initialize a project. This is done with the init command. Similar to git this will create a .mage directory where all the configuration, environments, custom tasks, and logs are stored.

Inside this directory there are a total of four directories:

  • .mage/config - the main general.yml configuration file is stored here.
  • .mage/config/environments - the environments configuration files are here.
  • .mage/logs - logs (if enabled) are saved there.
  • .mage/tasks - and custom tasks are stored over here.

mage init --name="My fantastic app" --email="notifications@my.app"


Right now the most important file is .mage/confg/general.yml. In there is stored the project name and notification email, and also if notification delivery and logging are enabled. The log files store everything Magallanes does, all commands and results, so if you need to debug something that's the place to start with. The maxlogs indicates how many logfiles are saved, when reached the older logs are deleted. These are quantity of files not days.

  • # global settings
  • name: My fantastic app
  • email: notifications@my.app
  • notifications: true
  • logging: true
  • maxlogs: 30



Add an Environment

Now the fun begins. We have our project initiated so now is it's time to add an environment. An environment is to where we want to deploy our code and what to do with it. So an environment will store the configuration of to where to copy the application and what to do with it once deployed.
Also we can configure our environment to work with releases, imagine it as a rudimentary versioning of our deployments.
We will talk about releases in a bit.

mage add environment --name="production" --enableReleases


With that last command we have just created a new environment named production.
This will create the configuration file .mage/config/environments/production.yml. We must edit that file in order to configure the environment.


  • # production environment
  • deployment:
    • user: dummy
    • from: ./
    • to: /var/www/vhosts/example.com/www
    • excludes:
      • - app/cache/*
      • - web/bundles/*
  • releases:
    • enabled: true
    • max: 10
    • symlink: current
    • directory: releases
  • hosts:
    • - 172.16.0.101
    • - 172.16.0.102
    • - 172.16.0.201:2222
  • tasks:
    • pre-deploy:
      • - scm/update
    • on-deploy:
      • - symfony2/cache-warmup: {env: prod}
    • post-release:
      • - purge-apc-cache
    • post-deploy:
      • - purge-varnish-cache

Also if you want to know which environments you have already configured, you can run the following command and it will display the existing environments.

mage list environments



Configuring an Environment

Following the previous example we will edit the .mage/config/environments/production.yml file to configure the environment.
Let's see the sections of the configuration file and what we can setup there.

deployment

In the deployment section we configure how we copy the application.
  • user - is the username used for the ssh connection.
  • from - is the location of the application, usually is the same directory where Magallanes is, but it could be any other path.
  • to - is the remote directory where the application is gonna be copied. If you are working with releases this shouldn’t be the document root.
  • excludes - is a list of files and directories to exclude in the rsync. Magallanes always excludes the .mage and .git directories.

hosts

In the hosts section we configure a list of hosts (IPs or hostnames) to where the rsync will run against. You can also specify a port number like in the example above. Also, note that you should be able to do an SSH connection to the host using ssh keys without passphrase, so you won't be requested any password.

tasks

In the tasks section we configure which tasks to run and when. You can look a complete list of built-in tasks here.
Each task is executed in the configured order and in a specific part of the deployment, these moments are these:
  • pre-deploy - before deployments begins. Useful for vendor installation and scm update tasks.
  • on-deploy - executed on each host after the code has been copied. Useful for cache warmup, symlinks creations, etc.
  • post-release - executed on each host after the release has been executed.
  • post-deploy - after the deployment is completed. Useful for general tasks like cleanup a cache system.
You can setup here your own tasks, bultin tasks always starts with a namespace of the task type it belongs (eg: scm, deployment, etc). On the other hand custom tasks don't have a namespace.

releases

In the releases section we configure how releases will work for this environment. We will look at this configuration in the releases section.

locking

When you run a deployment an instance with Magallanes, all further deployments (for any other environment in that instance) are locked. This prevents multiple possible problems that you may experience, like different branches being deployed, a mix of vendors, etc. After the deployment is done, the lock is released.

But sometimes you want to force locking the deployment to an environment, you can do this with the lock command.

mage lock to:production

This will lock all deployments to the production environment until you unlock it with the unlock command.

mage unlock to:production



Deployment

Deployment is the core functionality of Magallanes, so it should be the most simple thing to do.

mage deploy to:production

And that's it! You just have to say to what environment to want to deploy to, and Magallanes does the rest, based on the environment's configuration of course.

Behind that command Magallanes performs an rsync between the local files and the remote servers. This means that only the needed files will be synced, but if you use releases (which is recommended) all the files will be synced again, so this may be a little inefficient, so Magallanes realizes that and uses another strategy, creates a tar.gz file and only copies that, which is faster than rsync.

You can choose between both strategies by configuring it in the environment configuration, with the strategy parameter, which currently takes four options: rsync, targz, git-rebase, or disabled:

  • deployment:
    • strategy: rsync


  • deployment:
    • strategy: targz


  • deployment:
    • strategy: git-rebase


  • deployment:
    • strategy: disabled

By default it is not configured, so Magallanes chooses targz over rsync when using releases. But you can play with the configuration and test which works better for you.

The disabled strategy doesn't copy any files at all, this can be useful if you want to performance other tasks without moving any files around.
The git-rebase strategy assumes that in your hosts there is a working copy of a Git repository, and will perform a remote pull, conserving all files that might be untracked.



Working with Releases

Once you have your releases configured, working with them is really easy. Your application will be deployed to a directory called releases/{releaseId}, and a symbolic link called current will be created to that directory.
The {releaseId} is a timestamp (YMDhms) defined at runtime, but the releases and current are defined in the environment's configuration.

  • releases:
    • enabled: true
    • max: 5
    • symlink: current
    • directory: releases

The configuration is pretty much straight forward, first of all there is a specific releases block:

  • enabled - must be set to true to enable the releases feature (default false).
  • max - indicates how many releases will be saved, this option is useful if you box has little space. Exceeding releases are deleted after the deployment is done, so take that into account when calculating the space needed.
  • symlink - name of the symbolic link.
  • directory - name of the directory where the releases are stored.


The releases feature has a few commands and options for making deployments more easy.

The list command display all available releases for the given environment, indicating the date and time of the deploy, and also which is the current release.

mage releases list to:production


There is also an option for overriding a release, suppose you just made a deployment and forgot to add a very tiny line of code (a javascript call or css rule), doing the deployment again would be a waste of space. In this case the overrideRelease option comes handy. When used in the deployment, it will reuse the ReleaseId of the one which is set as current, therefore overwriting all the files. This option is very simple and powerful, so be very careful when using it.

mage deploy to:production --overrideRelease

Also it is possible to rollback a release to the previous release or to a specific release with the rollback command. More of this command in it's own section.



Rollback a Release

Sometimes mistakes are made, either willingly (deploying a feature not approved to production) or not (a plain old bug). This means we need to be able to go back to the previous release in an easy manner. Of course this could be fixed by disabling the unwanted feature in the repository and deploying again, but this flow is not efficient: you are leaving an "unwanted deploy" and also change back the code that you will have to change again later.

This is where the rollback command comes in action. Similar to a deploy, you have to indicate in which environment you want to do the rollback, and then to specify to which release you want to rollback to. The environment is indicated like in any other command to:environment, and then in the release parameter you specify the RelaseId you want to change to.

mage releases rollback --release=20120101172148 to:production

Given that the Release ID is a very big number, and they tend to look alike, it's easier to use a shortcut. If the release options is a negative number or zero, then it will be rolled back to that release index. Zero being the last release, -1 being the penultimate, -2 the antepenultimate, and so forth.

mage releases rollback --release=-1 to:production
mage releases rollback --release=-2 to:production
mage releases rollback --release=-3 to:production

So if you want to revert to the previous release before then use --release=-1, and if you want to go back to the last release deployed, use --release=0.

mage releases rollback --release=0 to:production


When a rollback is executed, the tasks you have defined are skipped unless you implement the Mage\Task\Releases\RollbackAware interface.

It's a common case when you deploy and the app fails, dreadfully fails, so you have to rollback. Ok, we have seen that that is easy. But you now have a broken release at "index 0". With the flag --deleteCurrent on the rollback command, you can delete that broken release.

mage releases rollback --release=-1 to:production --deleteCurrent

One last thing. A shortcut. Instead of calling the release rollback --release=XYZ command, you can just use rollback XYZ, it will be just the same, it's just a shortcut.

magerollback -1 to:production --deleteCurrent




Stages

Back when we discussed the Configuration of an environment, when a deployment runs, there are several stages in which the tasks are being executed.

Pre-Deploy

These tasks are executed on your local copy of the code. For example you can update your SCM Repository, change branches, compile assets, etc. All the heavy lifting -that doesn't require the code being in the final destination- should be done here.

On-Deploy

These tasks are executed on every host, this is where the actual deployment is done. The Deployment Strategy is executed here (rsync, tar, etc). These tasks will be executed on the remote code, for every host, but before being released. Cache warmups can be executed here.

Post-Release

After all the hosts had been deployed successfully, then the new code is released. These tasks are executed on every host after the release symlink is changed to the new code. You can use tasks here to cleanup APC cache or whatever you want. These tasks are only executed on each host, and only if you are using releases.

Post-Deploy

Finally, after the deployment is done, these tasks get to be executed. You can cleanup your local copy, or purge some general cache (like varnish). These tasks are executed locally.




Built In Tasks

SCM - Software Change Manager

Currently only GIT is supported. The following tasks are available:

  • scm/update - Updates the current working copy. Useful as a pre-deploy task for pull all changes before deploying.
  • scm/force-update - UDownloads the latest from remote without trying to merge or rebase anything. Resets the master branch to what you just fetched. Changes all the files in your working tree to match the files in origin/master, so if you have any local changes, they will be lost.


Symfony 2

The following tasks are available for interacting with the Symfony 2 console:

  • symfony2/assetic-dump - Dumps all Assetics.
  • symfony2/assets-install - Install the assets. Optional parameters: target, symlink, and relative.
  • symfony2/cache-clear - Clears the App Cache.
  • symfony2/cache-warmup - Warms up the App Cache.
In all cases the env option is available to specify the app environment, by default is dev.


Magento

The following tasks are available for usage with Magento:

  • magento/clear-cache - Clears Cache.
  • magento/clear-full-page-cache - Clears Full Page Cache.
This commands are executed in the local environment, not on the hosts.


Composer

The following tasks are available for usage with Composer, by default the executable is presumed to be php composer.phar in the root directory:

  • composer/generate-autoload - Generates de autload with the optimize flag.
  • composer/install - Installs the vendors.



Cookbook

Custom Tasks

Creating new tasks is a very important part of any good deployment tool. Every project has it's own necessities, so making custom tasks is essential. Luckily this is very easy with Magallanes.

Every custom task must be stored in your .mage/tasks directory. The Class can have any valid name, and extend the Mage\Task\AbstractTask class and be inside the Task namespace.
Then you have to implement two methods:

  • getName - this method returns the name of the task to be displayed while you run Magallanes.
  • run - this method does the actual work of the task.
And that's it! Then you can add your task to your environments, just by they class name: Permissions would be permissions, and DumpAssets would be dump-assets.

Here is a simple example:

<?php
namespace Task;

use Mage\Task\AbstractTask;

class Permissions extends AbstractTask
{
public function getName()
{
return 'Fixing file permissions';
}

public function run()
{
$command = 'chmod 755 . -R';
$result = $this->runCommandRemote($command);

return $result;
}
}

Take a look at the API Documentation, the examples tasks, and the ones already built in Magallanes to get some ideas of what kind of custom tasks you can create. But basically you can execute any command in your local copy of code and in the remote copy.


Custom Commands

Sometime you may need to create a new Command, with Magallanes this is very easy to do, just as easy as a task.

Every custom command must be stored in your .mage/commands directory (not created by default). The Class can have any valid name, and extend the Mage\Command\AbstractCommand class and be inside the Command namespace.
Then you have to implement one method:

  • run - this method does the actual work of the command.
And that's it! Then you can invoke your command from the command line.

Here is a simple example:

<?php
namespace Command;

use Mage\Command\AbstractCommand;
use Mage\Console;

class HelloWorld extends AbstractCommand
{
public function run()
{
Console::output('Hello world', 1, 2);

return 0;
}
}

Then you can just invoke it as:

mage hello-world

Built in commands have precedence over custom commands.
Take a look at the API Documentation, the examples command, and the ones already built in Magallanes to get some ideas of what kind of custom commands you can create.


Per Host Configuration

Sometimes you may want to change a little bit of configuration for a particular host, perhaps the deploy path, the user, or change the tasks to be executed, etc. Well, in Magallanes you can have a configuration per host, and this is how:

  • # production environment
  • deployment:
    • user: dummy
    • from: ./
    • to: /var/www/vhosts/example.com/www
    • excludes:
      • - app/cache/*
      • - web/bundles/*
  • releases:
    • enabled: true
    • max: 10
    • symlink: current
    • directory: releases
  • hosts:
    • - 172.16.0.101
    • 172.16.0.102:
      • deployment:
        • to: /home/sites/example.com/www
        • user: legacy
      • releases:
        • max: 5
        • symlink: httpdocs
      • tasks:
        • on-deploy:
          • - custom-cache-warmup
    • - 172.16.0.201:2222
  • tasks:
    • pre-deploy:
      • - scm/update
    • on-deploy:
      • - symfony2/cache-warmup: {env: prod}
    • post-release:
      • - purge-apc-cache
    • post-deploy:
      • - purge-varnish-cache

As you can see, for the host 172.16.0.102 we have overwritten some configurations. For example, the user and directory for the deployment are different; also the release symlink and maximum stored releases are also different. Finally the on-deploy tasks is different; the tasks are completely overwritten, these are not added to the others, and only the on-deploy and post-release stages can be overwritten.


Task Configuration

You may want to pass some parameters to your tasks, perhaps an environment option, a path, a password, anything. Luckily this is very easy to do in Magallanes, just pass along a YAML array on the task definition.

  • tasks:
    • on-deploy:
      • - symfony2/cache-warmup: {env: prod}

  • tasks:
    • on-deploy:
      • - symfony2/cache-warmup: {env: test}

  • tasks:
    • on-deploy:
      • - my-custom-task: {param: value}

Then you can retrieve the value in your task like this:

$env = $this->getParameter('env', 'dev');

Where env is your parameter name, and in this example, dev is your default value.