28 Nov 2018
Lately, I've been looking for a way to replace my dirty bash scripts used to deploy this blog on my VPS. A few years ago, I gave a shot to Capistrano and Fabric. They are great tools but written in languages I don't master (that's especially true for Capistrano). I also remember that I had problems with them when upgrading the Ruby and Python packages of my machine. This time, I wanted to try something else, something that could respect my expectations:
composer
and npm
on my server)git
on my server)At first glance Deployer seemed to answer my needs.
Apart from all the features I've already listed above, Deployer is easy to install (either by composer
or with a phar
package), highly configurable and very modular. It supports natively a lot of PHP frameworks and applications. A deployment is as easy as executing dep deploy
and using the option -v
lists all the underlying system commands that are actually launched. Last but no least, it's written in PHP. It may seem to be a detail, but it's very useful to dig into the code and to understand exactly what a recipe does.
In short, Deployer is simple, efficient and crystal clear.
Deployer ships with recipes for Symfony 2, 3 and 4. By default, a Symfony4 deployment performs the following actions on the server:
All those actions work out of the box as long as the recipe is correctly configured. Also, using an inventory file, allows to have a solid deployment script that can be versioned alongside the code.
That's great! But it's not yet what I need :)
The first thing I needed was to create a custom task to build my project locally:
desc('Build project');
task(
'build',
function () {
// build frontend
run('sh bin/build-front');
// build backend
run('composer install --no-ansi --no-dev --no-interaction --no-progress --no-scripts --optimize-autoloader');
}
)->local();
As I don't checkout the sources from the repository, I need to be able to upload them:
desc('Upload project');
task('upload', function () {
upload(__DIR__ . '/*', '', ['options' =>
[
"--exclude='.env'",
"--exclude='.git/'",
"--exclude='.gitignore'",
"--exclude='.idea/'",
"--exclude='.*.xml.dist'",
"--exclude='.*.yml'",
"--exclude='.*.yml.dist'",
"--exclude='phpstan.neon'",
"--exclude='semantic.json'",
"--exclude='symfony.lock'",
"--exclude='node_modules/'",
"--exclude='semantic/'",
"--exclude='tests/'",
"--exclude='var/'",
]
]);
});
It's very important to use the variable here (and not the variable
). For instance, if relates to `path/to/my/app/`, then
would be path/to/my/app/releases/X/
(where X is the Xth deployment).
Then, the last thing to do is to replace the default Symfony4 deploy
task by what I need:
- task('deploy', [
+ task('deploy', [ // override the default Symfony4 deploy task
+ 'build',
'deploy:info',
'deploy:prepare',
'deploy:lock',
'deploy:release',
- 'deploy:update_code',
+ 'upload',
'deploy:shared',
- 'deploy:vendors',
- 'deploy:writable', // I don't need any writable file or directory
'deploy:cache:clear',
'deploy:cache:warmup',
'deploy:symlink',
'deploy:unlock',
'cleanup',
]);
That's simple and works like a charm. The whole script is available here.
Next step will be to setup continuous delivery on Gitlab with Deployer. Maybe I'll have to change a few things on this deployment script.