Magallanes is a deployment tool made with PHP and for PHP applications, it's quite simple to use and manage.

Just like typing bin/mage deploy production

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, 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).

The mission with Magallanes is very simple, to move code from point A to point B (or several Bs) and group common tasks associated with the deployment workflow. Some of this tasks are quite common, like installing vendors and warming a cache, so there are some Built In Tasks for common usage. But you are encouraged to build your own... and share them!

As it has always been, Magallanes is open to Pull Requests, just make sure you write tests for any new feature and all the new code has the appropriate coverage.

The current version of Magallanes (v3) has been completely rewritten with Symfony3 Components, in particular Console, Process, FileSystem, YAML, and Finder. Also the logging is delegated to Monolog, and of course all the power of Composer to glue all together.

Installation

Installing Magallanes is quite simple, just add the dependency to your Composer configuration, update and you are done!

"require": {
    "andres-montanez/magallanes": "^3.0"
}

Also you can execute: composer require andres-montanez/magallanes:^3.0

After installing you can invoke Magallanes with the vendor/bin/mage executable, or you can configure Composer to link all vendors binaries into a more friendly directory, for example under bin adding this configuration to your composer.json

"config": {
    "bin-dir": "bin"
}

You will have to purge your vendors and install them again. Or you could just simply symlink to the binary ln -s vendor/bin/mage bin/mage

For the rest of the documentation let's assume your Magallanes executable is bin/mage

The most simple test is to check the installed version

bin/mage version

Magallanes v3.0.0 [Nostromo]

Configuration

All configuration is handled by one single YAML file in the root of the project: .mage.yml This file contains environment definitions, options and tasks configurations.

The following example is a very basic configuration with just one environment:

magephp:
    environments:
        production:
            user: app
            branch: master
            host_path: /var/www/myapp
            releases: 4
            exclude:
                - ./var/cache/*
                - ./var/log/*
                - ./web/app_dev.php
            hosts:
                - webserver1
                - webserver2
                - webserver3
            pre-deploy:
                - git/update
                - composer/install
                - composer/dump-autoload
            on-deploy:
                - symfony/cache-warmup: { env: 'dev' }
                - symfony/assets-install: { env: 'dev' }
                - symfony/assetic-dump: { env: 'dev' }
            on-release:
            post-release:
            post-deploy:

The first element of the configuration must be the magephp node. Then the environments section will hold all the configuration for your environments.

An environment is a set of configurations, there you will define where you want to deploy the code to, with which user, to which folder, to which hosts, of you need to checkout a specific branch (if using git), if you want to keep a history of the releases (deployed code) just in case you may need to rollback, also you have the chance to exclude some files if needed. And then a set of tasks to execute in specific order and stages of the deployment.

Also there are many configuration options which can be overwritten for your convenience.

Environments

The key of your environment will be it's name, so in the example above it is production, therefore you will deploy with bin/mage deploy production

Let's take a look at the configuration options

Option Description
user

It's the username to use on SSH and Rsync commands. Use a user who has enough privileges to write on the host.

If undefined then the current user (read with posix_getpwuid(posix_geteuid())) will be used.

branch

If you are using Git, and this option is defined, then Magallanes will try to checkout this branch for the deployment. It's useful if you have a development branch and environment.

If left undefined no git task will be executed.

host_path

It's the path on the remote hosts to where the code is going to be deployed. This parameter is shared with all hosts.

If left undefined a DeploymentException will be thrown.

ssh Configuration overwrite for SSH commands. See following section Configuration » SSH.
sudo If set to true all remote commands will be prefixed with sudo. Default is false
tar_create

Configuration overwrite for flags on Tar creation. See following section Configuration » Tar.

If left undefined a cfzp will be used.

tar_extract

Configuration overwrite for flags on Tar extraction. See following section Configuration » Tar.

If left undefined a xfzop will be used.

rsync

Configuration overwrite for Rsync flags. See following section Configuration » Rsync.

If left undefined a -avz will be used.

releases

If you want to enable Releases and in consequence the option to perform Rollback, then define this parameter indicating how many releases you want to leave on the hosts. The greater the number the more back in time you will be able to rollback, but it will also consume more storage.

By default releases are disabled.

exclude A list of files or directories to exclude when doing Rsync or when creating the Tar for the deployment. Be careful with the wildcard and for better control always start with ./ so you are sure it's from the root onwards.
hosts

The list of hosts to which the code is going to be deployed. It can be an empty list, if so all deployment tasks will be skipped.

Make sure you can connect passwordless to all of them using SSH keys.

pre-deploy

on-deploy

on-release

post-release

post-deploy

The list tasks to be executed on each stage. The list can be empty, Magallanes still will add some tasks by itself in order to run the deployment process.

You can have as many environments as you want. If using several branches make sure to always have the same .mage.yml file across then all, otherwise the in-memory configuration will not be updated.

SSH

Many tasks rely on SSH connection, like scp, rsync and of course executing remote commands with ssh. You can configure the following options for each environment. Each options is shown with it's default value.

magephp:
    environments:
        production:
            ssh:
                port: 22
                flags: -q -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no

You can use the flags option to tweak your SSH connection as best fits your needs. The best way to make your connection is by using SSH Keys across your servers. These flags are also inherited by the Rsync and Tar tasks.

Tar

The Tar tasks uses two set of flags, one for creation of the file, and another for it's extraction. By default the creation flags are cfzop and the extraction flags are zfzop, you can overwrite them if you need to in the environment section.

magephp:
    environments:
        production:
            tar_create: cfh
            tar_extract: xf

Rsync

The Rsync task uses by default the -avz flags, you can overwrite them if you need to in the environment section.

magephp:
    environments:
        production:
            rsync: -avz

Logging

Logging is optional but encouraged nonetheless, you can enable them by defining this general option:

magephp:
    log_dir: /path/to/my/logs

The log_dir option is a directory where all the logs will be generated, at the beginning of the deployment the current log file will be informed.

Releases

When using Releases you have to take into account that Magallanes will create a releases directory in the host_path directory, and a symbolic link current which will point to a release inside releases.

The code will be deployed to a directory named after a Release Id, which is just a convenient timestamp.

This is a directory tree example:

/var/www/app/
  • current -> releases/20170102121530
  • releases/
    • 20170101061530/
    • 20170101081530/
    • 20170102061530/
    • 20170102121530/
File/Folder Purpose
/var/www/app/ The host_path directory, defined in the configuration
releases/ Contains all the deployed releases
releases/20170102121530/ A deployed release
current A symbolic link pointing to the live release

Take into account this when configuring Virtual Hosts, cronjob paths, etc.

Workflow

Overview

Nice flowcharts coming soon ¯\_(ツ)_/¯

Stages

Magallanes works in Stages. Each stage is a step in the deployment process. Each stage has a context of execution, and within that context, some tasks will be executed.

The Local context is when by default tasks will operate on the directory where you are launching the deployment. On the other end, Remote context is when by default tasks will be executed on the current working remote host, to which the code is being deployed to.

Name Code Context Description Examples
Pre Deploy pre-deploy Local This is the first stage in the deployment. Tasks in this stage will be the firsts to be executed. Pulling from repository, switching branches, installing vendors
On Deploy on-deploy Remote This is the second stage. Tasks in this stage will be the firsts to be executed on each host. This stage will be triggered for each defined deployment host. Warming cache, installing assets, switching configuration
On Release on-release Remote This stage is optional, and will be executed only if Releases are enabled. Tasks in this stage will be executed on each host at the moment of swapping the current release. This stage will be triggered for each defined deployment host. Puring APC Cache
Post Release post-release Remote This stage is optional, and will be executed only if Releases are enabled, and after all releases where swapped successfully on each host. Tasks in this stage will be executed on each host after the swapping the current release. This stage will be triggered for each defined deployment host. Removing locks, purging cache
Post Deploy post-deploy Local This is the last stage in the deployment. Tasks in this stage will be the lasts to be executed. Purging cache

Strategies

Magallanes uses two strategies for making the deployments. This is inferred from the configuration and currently can not be configured. If you have releases enabled then the Releases Strategy will be used, which copy the code using a tar file and scp.

On the other hand if you don't use Releases then Magallanes will use the Rsync Strategy which copy the code using rsync command.

Also there are a few restrictions with the Rsync Strategy, the tasks defined on the on-release and post-release stages will not be executed because releases are not enabled so it doesnt' make sense to execute tasks associated with releases stages.

Commands

Version

Quite self explanatory, this command will tell you which version of Magallanes is being used.

bin/mage version

Magallanes v3.0.0 [Nostromo]

Config

The Config is useful for debugging, it offers two subcommands: dump and environments

Dump

This will dump your .mage.yml file as interpreted by the Yaml component, in a PHP array export. This is the interpreted output of the sample configuration.

bin/mage config:dump

Starting Magallanes

array (
  'environments' =>
  array (
    'production' =>
    array (
      'user' => 'app',
      'branch' => 'master',
      'host_path' => '/var/www/myapp',
      'releases' => 4,
      'exclude' =>
      array (
        0 => './var/cache/*',
        1 => './var/log/*',
        2 => './web/app_dev.php',
      ),
      'hosts' =>
      array (
        0 => 'webserver1',
        1 => 'webserver2',
        2 => 'webserver3',
      ),
      'pre-deploy' =>
      array (
        0 => 'git/update',
        1 => 'composer/install',
        2 => 'composer/dump-autoload',
      ),
      'on-deploy' =>
      array (
        0 =>
        array (
          'symfony/cache-warmup' =>
          array (
            'env' => 'dev',
          ),
        ),
        1 =>
        array (
          'symfony/assets-install' =>
          array (
            'env' => 'dev',
          ),
        ),
        2 =>
        array (
          'symfony/assetic-dump' =>
          array (
            'env' => 'dev',
          ),
        ),
      ),
      'on-release' => NULL,
      'post-release' => NULL,
      'post-deploy' => NULL,
    ),
  ),
)

Finished Magallanes


Environments

This other option will list all the defined environments and some handy information: defined user, branch, and hosts list. This is the output for the sample configuration.

bin/mage config:environments

Starting Magallanes

+-------------+------+--------+------------+
| Environment | User | Branch | Hosts      |
+-------------+------+--------+------------+
| production  | app  | master | webserver1 |
|             |      |        | webserver2 |
|             |      |        | webserver3 |
+-------------+------+--------+------------+

Finished Magallanes

Deploy

Well, this is the big one! The deploy command will start the deployment of your code. You just have to provide to which environment you want to deploy to:

bin/mage deploy production and that's it! You have done it!!

Optionally you can set a branch with --branch=test which will override your configuration, if you don't have a branch configured it will be set to the one provided.

Here is a complete output based on the sample configuration form above

bin/mage deploy production --branch=test

Starting Magallanes

    Environment: production
    Release ID: 20170104042540
    Strategy: Releases
    Branch: test

    Starting Pre Deploy tasks:
        Running [Git] Change Branch (test) ... OK
        Running [Git] Update ... OK
        Running [Composer] Install ... OK
        Running [Composer] Generate Autoload ... OK
        Running [Deploy] Preparing Tar file ... OK
    Finished 5/5 tasks for Pre Deploy.

    Starting On Deploy tasks on host webserver1:
        Running [Release] Preparing Release ... OK
        Running [Deploy] Copying files with Tar ... OK
        Running [Symfony] Cache Warmup ... OK
        Running [Symfony] Assets Install ... OK
        Running [Symfony] Assetic Dump ... OK
    Finished 5/5 tasks for On Deploy.

    Starting On Deploy tasks on host webserver2:
        Running [Release] Preparing Release ... OK
        Running [Deploy] Copying files with Tar ... OK
        Running [Symfony] Cache Warmup ... OK
        Running [Symfony] Assets Install ... OK
        Running [Symfony] Assetic Dump ... OK
    Finished 5/5 tasks for On Deploy.

    Starting On Deploy tasks on host webserver3:
        Running [Release] Preparing Release ... OK
        Running [Deploy] Copying files with Tar ... OK
        Running [Symfony] Cache Warmup ... OK
        Running [Symfony] Assets Install ... OK
        Running [Symfony] Assetic Dump ... OK
    Finished 5/5 tasks for On Deploy.

    Starting On Release tasks on host webserver1:
        Running [Release] Creating Symlink ... OK
    Finished 1/1 tasks for On Release.

    Starting On Release tasks on host webserver2:
        Running [Release] Creating Symlink ... OK
    Finished 1/1 tasks for On Release.

    Starting On Release tasks on host webserver3:
        Running [Release] Creating Symlink ... OK
    Finished 1/1 tasks for On Release.

    Starting Post Release tasks on host webserver1:
        Running [Release] Cleaning up old Releases ... OK
    Finished 1/1 tasks for On Release.

    Starting Post Release tasks on host webserver2:
        Running [Release] Cleaning up old Releases ... OK
    Finished 1/1 tasks for Post Release.

    Starting Post Release tasks on host webserver3:
        Running [Release] Cleaning up old Releases ... OK
    Finished 1/1 tasks for Post Release.

    Starting Post Deploy tasks:
        Running [Deploy] Cleanup Tar file ... OK
        Running [Git] Change Branch (master) ... OK
    Finished 2/2 tasks for Post Deploy.

Finished Magallanes

As you can see there are many tasks added by Magallanes. This configuration has Releases enabled so the deployment will be executed with a Tar file copied to all defined hosts. On the Pre Deploy stage the Tar file is created, then on each On Deploy iteration the file is copied, and finally the file is deleted on the Post Deploy stage.

Also, because Releases are enabled, Magallanes needs to create the release directory, swap the current symlink, and then delete old releases. On the same fashion, the first task of the On Deploy stage is to create the directory, then the On Release stage the symlink is swapped, and after all releases were completed successfully the delete process is triggered on the Post Release stage.

A similar process is made for changing branches, but on the Pre Deploy and Post Deploy stages.

Releases

The Releases command is in charge of managing your deployed releases, it offers two subcommands: list and rollback

List

This will list your releases on a given environment. This is the list output for the sample configuration.

bin/mage releases:list production

Starting Magallanes

    Environment: production

    Releases on host webserver1:
        Release ID: 20170102121530 - Date: 2017-01-02 12:15:30 [2 hour(s) ago] [current]
        Release ID: 20170102061530 - Date: 2017-01-02 06:15:30 [8 hour(s) ago]
        Release ID: 20170101081530 - Date: 2017-01-01 08:15:30 [1 day(s) ago]
        Release ID: 20170101061530 - Date: 2017-01-01 06:15:30 [1 day(s) ago]

    Releases on host webserver2:
        Release ID: 20170102121530 - Date: 2017-01-02 12:15:30 [2 hour(s) ago] [current]
        Release ID: 20170102061530 - Date: 2017-01-02 06:15:30 [8 hour(s) ago]
        Release ID: 20170101081530 - Date: 2017-01-01 08:15:30 [1 day(s) ago]
        Release ID: 20170101061530 - Date: 2017-01-01 06:15:30 [1 day(s) ago]

    Releases on host webserver3:
        Release ID: 20170102121530 - Date: 2017-01-02 12:15:30 [2 hour(s) ago] [current]
        Release ID: 20170102061530 - Date: 2017-01-02 06:15:30 [8 hour(s) ago]
        Release ID: 20170101081530 - Date: 2017-01-01 08:15:30 [1 day(s) ago]
        Release ID: 20170101061530 - Date: 2017-01-01 06:15:30 [1 day(s) ago]

Finished Magallanes

The [current] label indicates that that's the release being used at the moment.


Rollback

Rollback is the other big command, it allows you to swap the current deployment to another release, already stored on your hosts.

The command besides needing the environment it also requires the Release Id to which you want to change the current deployment. It must exists on all your hosts. This is the rollback output for the sample configuration.

bin/mage releases:rollback production 20170101081530

Starting Magallanes

    Environment: production
    Rollback to Release ID: 20170101081530
    Strategy: Releases

    Starting Pre Deploy tasks:
        Running [Git] Update ... SKIPPED
        Running [Composer] Install ... SKIPPED
        Running [Composer] Generate Autoload ... SKIPPED
    Finished 3/3 tasks for Pre Deploy.

    No tasks defined for On Deploy stage

    Starting On Release tasks on host webserver1:
        Running [Release] Creating Symlink ... OK
    Finished 1/1 tasks for On Release.

    Starting On Release tasks on host webserver2:
        Running [Release] Creating Symlink ... OK
    Finished 1/1 tasks for On Release.

    Starting On Release tasks on host webserver3:
        Running [Release] Creating Symlink ... OK
    Finished 1/1 tasks for On Release.

    No tasks defined for Post Release stage

    No tasks defined for Post Deploy stage

Finished Magallanes

As you can compare with the output from the deployment, many of the tasks are skipped because they only make sense when doing a deployment.

Also take notice that all stages are executed, but many will not have tasks.

BuiltIn Tasks

Deploy

The Deployment Tasks are automatically used by Magallanes when preparing the tasks that needs to be executed for the deployment.

You can use this tasks to tweak the order of execution. For example, on a deployment without releases, Magallanes adds deploy/rsync as the first task of the On Deploy stage, but you may want to run a custom task before that, so you can add deploy/rsync as the second task in the list and Magallanes will respect the order you defined.

In most cases you will not need to manually use these tasks.

Name Stage Description
deploy/release/prepare On Deploy When releases are enabled, this task will create the appropriate directory on each host.
deploy/release On Release When releases are enabled, this task will create the symlink current to the appropriate directory on each host.
deploy/release/cleanup Post Release When releases are enabled, this task will look up all the releases directories on all hosts and if it has passed the number of defined releases it will delete the oldest.
deploy/rsync On Deploy When releases are not enabled the deployment will be copied with rsync, this task will sync the code to all hosts.
deploy/tar/prepare Pre Deploy When releases are enabled, this task will create a zipped tarball file with tar.
deploy/tar/copy On Deploy When releases are enabled, this task will copy with scp the tarball file created earlier to all defined hosts.
deploy/tar/cleanup Post Deploy When releases are enabled, after the deployment is complete, this task will delete the tarball file created earlier.

GIT

This task are for interacting with your GIT repository, it's most likely that you will use git/update at the beginning of your Pre Deploy tasks. Magallanes will automatically add the git/change-branch task if needed.

Name Stage Parameters Description
git/update Pre Deploy - Use this task to update your branch on the Pre Deploy stage, it will git pull the code for you.
git/change-branch

Pre Deploy

Post Deploy

branch This task is automatically added by Magallanes at the beginning of the Pre Deploy stage if you have the option branch defined. It will look if the current checked out branch and skip if it's the same as branch or change to it with git checkout branch. If the switch was made, the task will remember the original branch and reverse it at the Post Deploy stage. This is handled automatically by Magallanes.

Composer

Composer has become an integral part of our PHP applications, so is handy to have some built in tasks for interacting with Composer.

By default it is assumed that Composer is installed globally so it will be invoked with composer, but if you have it in an specific path you can configure it at root level or at environment level under the composer section with the path parameter.

magephp:
    composer:
        path: /alternative/path-to/composer.phar
    environments:
        production:
            composer:
                path: /usr/bin/composer

The following tasks are available in Magallanes for using Composer.

Name Parameters Description
composer/install

path default composer

flags default --optimize-autoloader

Will execute composer install plus the given flags.
composer/dump-autoload

path default composer

flags default --optimize

Will execute composer dump-autoload plus the given flags.
composer/self-update

path default composer

days default 60

Will execute composer self-update to the latest version. The task will skip if Composer is up-to-date.

The parameters can be specified at the task level, for instance if you want to change the default flags for the composer/install task, you could do the following.

magephp:
    environments:
        production:
            pre-deploy:
                - composer/install: { flags: '--no-dev' }

Symfony

Magallaes has been rebuilt with Symfony3 Components, so I like the framework a lot! It seems only fair to have some built in tasks for common Symfony integration.

By default it is assumed that the Symfony Console is located in bin/console, following the Symfony3 directory structure, but if you have it in an specific path you can configure it at root level under the symfony section with the console parameter.

magephp:
    symfony:
        console: app/api-console
Name Parameters Description
symfony/cache-clear

console default bin/console

env default dev

flags default empty

Will clear the appropriate environment's cache by executing bin/console cache:clear --env=ENV plus any given flags.
symfony/cache-warmup

console default bin/console

env default dev

flags default empty

Will warm up the appropriate environment's cache by executing bin/console cache:warmup --env=ENV plus any given flags.
symfony/assets-install

console default bin/console

env default dev

target default web

flags default --symlink --relative

Will install the assets defined by the loaded bundles by executing bin/console assets:install TARGET --env=ENV plus the given flags.
symfony/assetic-dump

console default bin/console

env default dev

Will dump assetics by executing bin/console assetic:dump --env=ENV plus any given flags.

Defining flags or environments at a global level is not useful, so you can define these parameters right along the task, just like the following example.

magephp:
    environments:
        test:
            on-deploy:
                - symfony/cache-warmup: { env: 'test' }

On the other hand, Symfony parameters can also exists at the Environment's level, so you could define an environment's env parameter for all you Symfony tasks and save a bit of configuration, just like this.

magephp:
    environments:
        production:
            symfony: { env: 'prod' }
            on-deploy:
                - symfony/cache-warmup

File System

Simple file system operations are common in the workflow of a deployment, for example coping a configuration file for a specific environment, or removing a lock file, or moving one file around.

Magallanes provides three simple operations: copy, move, and remove. Besides adding this operations to your deployment process you gain the advantage of defining the file paths with variables dependant of the deployment.

magephp:
    environments:
        production:
            on-deploy:
                - fs/copy: { from: 'app/config/envs/%environment%.yml', to: 'app/config/parameters.yml' }

The example above, when ran with bin/mage deploy production the from path will be converted to app/config/envs/production.yml

The supported variables are: %environment%, %host%, %release%.

Name Parameters Description
fs/copy

from file path

to file path

flags default -p

Will copy a file executing cp -p FROM TO, be careful if changing the flags.
fs/link

from file path

to file path

flags default -snf

Will symlink a file executing ln -snf FROM TO, be careful if changing the flags.
fs/move

from file path

to file path

flags default empty

Will move a file executing mv FROM TO, plus the given flags.
fs/remove

file file path

flags default empty

Will delete a file executing rm FILE, plus the given flags.
fs/chmod

file file path

mode mode to set

flags default empty

Will change a file's mode executing chmod MODE FILE, plus the given flags.

These commands are executed according to the stage they are placed on. If defined on pre-deployment and post-deployment the tasks are going to be executed locally, otherwise they are going to be executed on each host inside the host_path plus the release directory path if releases are enabled.

Execute

Sometimes you need to execute a command, a shell script, or perhaps restart a service. You could create a custom task for that, but with this task you can configure Magallanes to do it easily.

magephp:
    environments:
        production:
            post-deploy:
                - exec: { cmd: './reload-docker.sh', desc: 'Reload Docker instances' }

The example above, when ran with ./reload-docker.sh locally on the post-deploy stage. The command will be executed locally or remotely based on the stage in which the task is defined.

The following are the parameters available for this task.

Parameter Default Description
cmd empty The command line to execute. This parameter is mandatory.
desc empty A description to output on the task execution.
timeout 120 Time out wait for the command execution, if the task takes too long you can tweak it here.

Cookbook

Custom Tasks

Magallanes is a powerful tool just as it is, but to really unleash it's potential you have to push it a little more. With custom tasks you can do whatever you want whenever you want.

Just like the built in tasks you can create your own, anywhere in your code. These tasks must comply two requirements: extend the class Mage\Task\AbstractTask and be loaded with Composer's autoload, and just that!

Let's see a very basic example.

<?php
namespace AppBundle\Library\Deployment;

use Symfony\Component\Process\Process;
use Mage\Task\Exception\ErrorException;
use Mage\Task\AbstractTask;

class PurgeMemcachedTask extends AbstractTask {
    public function getName()
    {
        return 'custom/purge-memcached';
    }

    public function getDescription()
    {
        return '[Custom] Purging Memcached';
    }

    public function execute()
    {
        if (!array_key_exists('server', $this->options) || !array_key_exists('port', $this->options)) {
            throw new ErrorException('Parameters "server" and "port" are required.');
        }

        $cmd = sprintf('echo "flush_all" | netcat %s %d', $this->options['server'], $this->options['port']);

        /** @var Process $process */
        $process = $this->runtime->runCommand($cmd);
        return $process->isSuccessful();
    }
}

You will be able to use this task as follows

magephp:
    environments:
        production:
            post-deploy:
                - 'AppBundle\Library\Deployment\PurgeMemcachedTask': { server: '10.0.0.50', port: 11211 }

Highlighted in the PHP code above you will notices some interesting things. First of all the Task Class is pretty simple, it extends Mage\Task\AbstractTask and implements three methods: getName, getDescription, and execute. Then the $this->options property is an array filled with the parameters defined on the task declaration, { server: '10.0.0.50', port: 11211 } in this example.

The server and port parameters are needed for the execution of the task, so if these weren't defined an exception is thrown, false could have been returned at that point but the advantage of throwing ErrorException is that the message will be displayed.

All tasks have access to the $this->runtime property, which is an instance of Mage\Runtime\Runtime, this instance has everything about what's going on in Magallanes, and also allows you to execute command line sentences. When you invoke the runCommand method the Runtime instance will lookup in which stage it is, if it is Pre Deploy or Post Deploy then the command will be execute locally, otherwise it will be executed remotely on each defined host. The process returned is an instance of the Process Symfony Component.

Finally a boolean status is returned based on the execution of the command, the isSuccessful will return true or false and this will mark the task as OK or FAIL respectively.

Custom Configuration

In the same lines as Custom Tasks, having an elastic configuration is also handy. Suppose you have a Custom Task that needs a bunch of parameters, or that you want to configure it globally and not for each environment. Well that's just plain easy.

Let's go for the first case, and following the Custom Task defined earlier, you want to be able to set the server and port on each environment and call the task twice.

magephp:
    environments:
        production:
            memcached: { server: '10.0.0.50', port: 11211 }
            pre-deploy:
                - 'AppBundle\Library\Deployment\PurgeMemcachedTask'
            post-deploy:
                - 'AppBundle\Library\Deployment\PurgeMemcachedTask'

In theory this will trigger your custom task at the beginning and at the end of the deployment, and you have added a memcached section at the environment level. Now let's tweak the Custom Task to read from that definition.

<?php
namespace AppBundle\Library\Deployment;

use Symfony\Component\Process\Process;
use Mage\Task\Exception\ErrorException;
use Mage\Task\AbstractTask;

class PurgeMemcachedTask extends AbstractTask {
    public function getName()
    {
        return 'custom/purge-memcached';
    }

    public function getDescription()
    {
        return '[Custom] Purging Memcached';
    }

    public function execute()
    {
        $options = $this->runtime->getEnvOption('memcached', []);
        if (!is_array($options) || !array_key_exists('server', $options) || !array_key_exists('port', $options)) {
            throw new ErrorException('Parameters "server" and "port" are required.');
        }

        $cmd = sprintf('echo "flush_all" | netcat %s %d', $options['server'], $options['port']);

        /** @var Process $process */
        $process = $this->runtime->runCommand($cmd);
        return $process->isSuccessful();
    }
}

Only three lines of code changed! Amazing! The getEnvOption method will look for the given key at the current environment, and return the second argument as default. Given that the value returned can be anything there is the extra check is_array($options) to make sure we are dealing with an array.

In the folloing case let's move the configuration globally.

magephp:
    memcached: { server: '10.0.0.50', port: 11211 }
    environments:
        production:
            post-deploy:
                - 'AppBundle\Library\Deployment\PurgeMemcachedTask'
        pre-production:
            post-deploy:
                - 'AppBundle\Library\Deployment\PurgeMemcachedTask'
<?php
namespace AppBundle\Library\Deployment;

use Symfony\Component\Process\Process;
use Mage\Task\Exception\ErrorException;
use Mage\Task\AbstractTask;

class PurgeMemcachedTask extends AbstractTask {
    public function getName()
    {
        return 'custom/purge-memcached';
    }

    public function getDescription()
    {
        return '[Custom] Purging Memcached';
    }

    public function execute()
    {
        $options = $this->runtime->getConfigOption('memcached', []);
        if (!is_array($options) || !array_key_exists('server', $options) || !array_key_exists('port', $options)) {
            throw new ErrorException('Parameters "server" and "port" are required.');
        }

        $cmd = sprintf('echo "flush_all" | netcat %s %d', $options['server'], $options['port']);

        /** @var Process $process */
        $process = $this->runtime->runCommand($cmd);
        return $process->isSuccessful();
    }
}

With the getConfigOption method will look for the given key at the root of the configuration, and return the second argument as default. Having a configuration in this level can be useful if you need a shared key for all the environments or a common path to a binary, etc.

Changelog

Nostromo - v3 Series

v3.1.0
  • Add new Exec task to execute arbitrary shell commands
  • Add new Composer task, to update phar (composer/self-update)
  • Allow to flag Filesystem tasks
  • Add new File System task, to change file's modes (fs/chmod)
  • Ignore empty exclude lines
  • Allow Composer task options to be overwritten at environment level
  • Add new method Runtime::getMergedOption to merge ConfigOption and EnvOption
v3.0.1
  • Fix escape issue when commands are sent through SSH
v3.0.0
  • Added all this awesome code
  • Not compatible with v1

Thanks

I want to give thanks to all the PHP community, those unsung heroes who continuously push the boundaries of technology.

And special thanks to my team mates in Acilia Internet for they continuing support, ideas, and challenges that made this project came to be in the first place.