How to use Deployer to deploy an Akeneo PIM installation
Akeneo is an open source product inventory managment (PIM) software. I’ve already written about how to install/setup a Linux installation with Akeneo. This time, I’ll explain how to use Deployer to do a new installation:
Performing a new Akeneo installation should be:
- Fast and atomic (guaranteed to be isolated from concurrent processes).
- Idempotent as much as possible when installing the same codebase.
Ignoring config cacheand cache warming here. Not sure if idempotence is the best term here, but for the lack of a better one: installing the same code multiple times should not do any harm. - Being able to do a rollback.
Luckily, the PHP deployment tool Deployer takes all care of this.
Akeneo installation with Deployer
I’ve done this setup on macOS High Sierra. Most of the steps are OS agnostic (unless you’re on Windows), but you’ll never know. I assume you’ve already installed/deployed an initial installation of Akeneo on your remote server at least once.
Start with installing Deployer and some dependencies:
# cd to project root# install deployer
composer require deployer/deployer# install recipes ('deployment templates')
composer require deployer/recipe# create deploy.php in root
dep init
Now open deploy.php
with your IDE/editor of choice. This file contains the deployment configuration and all steps of which a deployment constitutes of.
At the top of the file, require the recipes. Since Akeneo is based on Symfony 3, we can use a Symfony deployment template. Akeneo uses Yarn to compile frontend assets.
require 'recipe/symfony3.php';
require 'recipe/yarn.php'
Since a file committed in a repository should never contain sensitive information, like passwords, use use a .env
file to store those. We need this info during the deployment (i.e. in deploy.php
), so we load the contents of the .env
file using Symfony’s Dotenv Component:
// Load .env
$dotenv = new \Symfony\Component\Dotenv\Dotenv();
$dotenv->load(__DIR__ . '/.env');
Then set some settings at the top of deploy.php
:
set('ssh_type', 'native');
set('ssh_multiplexing', true);
set('default_stage', 'production');
set('application', getenv('APP_NAME'));
set('repository', getenv('GIT_REPOSITORY'));
set('shared_files', ['app/config/parameters.yml', '.env']);
The capitalized variables are variables we have to create in .env
. Let add those to .env
and some we need later. Replace the [...]
values by your own:
# .env configuration
# do NOT commit this file to the repo
# add .env line to your .gitignoreAPP_NAME=Akeneo
APP_ENV=localGIT_REPOSITORY=[GIT_REPOSITORY_URL]REMOTE_USER=[SSH_USERNAME]
REMOTE_HOSTNAME=[SSH_HOSTNAME]
REMOTE_DEPLOY_PATH=[REMOTE_DEPLOY_PATH]
Then, back in deploy.php
, configure a host. We only configure a production
host for the sake of brevity. You could/should make a staging
entry too.
// Production
host('production')
->user(getenv('REMOTE_USER'))
->configFile('~/.ssh/config')
->identityFile('~/.ssh/id_rsa')
->hostname(getenv('REMOTE_HOSTNAME'))
->stage('production')
->set('branch', 'master')
->set('deploy_path', getenv('REMOTE_DEPLOY_PATH') . '/production');
Then configure the task list in deploy.php
. Most of this was taken from the Symonfy 3 recipe, with some additions for Akeneo:
// Tasks
task('deploy', [
'deploy:info',
'deploy:confirm',
'deploy:prepare',
'deploy:lock',
'deploy:release',
'deploy:update_code',
'deploy:clear_paths',
'deploy:create_cache_dir',
'deploy:shared',
'deploy:assets',
'deploy:vendors',
'pim:installer:dump-require-paths',
'pim:installer:assets',
'yarn:install',
'yarn:compile',
'deploy:assets:install',
'deploy:assetic:dump',
'deploy:cache:clear',
'deploy:cache:warmup',
'deploy:writable',
'database:migrate',
'deploy:symlink',
'php:restart',
'deploy:unlock',
'cleanup',
]);
Then add a task to run Webpack with Yarn:
// Custom task yarn:compile
desc('Execute yarn:compile');
task('yarn:compile', function () {
run('cd {{release_path}} && {{bin/yarn}} run webpack');
});
Then add an optional task to start the deployment with a confirmation question:
// Custom task to confirm
desc('Please confirm the deployment');
task('deploy:confirm', function () {
if (! askConfirmation('Are you sure you want to deploy?', true)) {
die('As you wish. Bye.');
}
});
Then some tasks to dump required paths and install assets, as described in Akeneo’s installation guide.
// Custom task to dump require paths
desc('Execute pim:installer:dump-require-paths');
task('pim:installer:dump-require-paths', function () {
run('{{bin/php}} {{bin/console}} pim:installer:dump-require-paths');
});// Custom task to dump require paths
desc('Execute pim:installer:assets');
task('pim:installer:assets', function () {
run('{{bin/php}} {{bin/console}} pim:installer:assets --symlink --clean {{console_options}}');
});
Then an optional task to restart PHP FPM. This is needed to prevent Symonfy from caching some Twig template files very aggressively. You won’t need this if you won’t modify your Akeneo codebase.
// Custom task to restart PHP FPM to fix a
// very aggressive cache of Twig templates.
desc('Restart PHP FPM');
task('php:restart', function () {
run('sudo /usr/sbin/service php7.1-fpm restart');
});
Then make sure Deployer releases the lock when a deployment fails:
// Failure
after('deploy:failed', 'deploy:unlock');
Executing
Open your terminal and go to your project directory. Then:
dep deploy production
will start a deployment to production
, invoked from your local machine.
In dep deploy production
, the latter is the hostname you configured in Deployer’s host(...)
function.
Final result
Tada 🎉
Full deploy.php
:
See my gist too.
<?php
namespace Deployer;
require 'recipe/symfony3.php';
require 'recipe/yarn.php';
// Load .env
$dotenv = new \Symfony\Component\Dotenv\Dotenv();
$dotenv->load(__DIR__ . '/.env');
// Settings
set('ssh_type', 'native');
set('ssh_multiplexing', true);
set('default_stage', 'production');
set('application', getenv('APP_NAME'));
set('repository', getenv('GIT_REPOSITORY'));// Added .env to default shared_files from recipe,
// remove this line if you won't be using .env on remote server
set('shared_files', ['app/config/parameters.yml', '.env']);
// Production
host('production')
->user(getenv('REMOTE_USER'))
->configFile('~/.ssh/config')
->identityFile('~/.ssh/id_rsa')
->hostname(getenv('REMOTE_HOSTNAME'))
->stage('production')
->set('branch', 'master')
->set('deploy_path', getenv('REMOTE_DEPLOY_PATH') . '/production');
// Tasks
task('deploy', [
'deploy:info',
'deploy:confirm',
'deploy:prepare',
'deploy:lock',
'deploy:release',
'deploy:update_code',
'deploy:clear_paths',
'deploy:create_cache_dir',
'deploy:shared',
'deploy:assets',
'deploy:vendors',
'pim:installer:dump-require-paths',
'pim:installer:assets',
'yarn:install',
'yarn:compile',
'deploy:assets:install',
'deploy:assetic:dump',
'deploy:cache:clear',
'deploy:cache:warmup',
'deploy:writable',
'database:migrate',
'deploy:symlink',
'php:restart',
'deploy:unlock',
'cleanup',
]);
// Custom task yarn:compile
desc('Execute yarn:compile');
task('yarn:compile', function () {
run('cd {{release_path}} && {{bin/yarn}} run webpack');
});
// Custom task to confirm
desc('Please confirm the deployment');
task('deploy:confirm', function () {
if (! askConfirmation('Are you sure you want to deploy?', true)) {
die('As you wish. Bye.');
}
});
// Custom task to dump require paths
desc('Execute pim:installer:dump-require-paths');
task('pim:installer:dump-require-paths', function () {
run('{{bin/php}} {{bin/console}} pim:installer:dump-require-paths');
});
// Custom task to dump require paths
desc('Execute pim:installer:assets');
task('pim:installer:assets', function () {
run('{{bin/php}} {{bin/console}} pim:installer:assets --symlink --clean {{console_options}}');
});
// Custom task to restart PHP FPM to fix a
// very aggressive cache of Twig templates.
desc('Restart PHP FPM');
task('php:restart', function () {
run('sudo /usr/sbin/service php7.1-fpm restart');
});
// Failure
after('deploy:failed', 'deploy:unlock');