In recent years, organisations have moved to use an approach of Continuous Delivery and Continuous Integration to deploy applications. This whole area of technology and development strategy, sometimes referred to as DevOps, is quite broad. Tech-sector companies have come to utterly rely on it as a plank of their ability to develop and deploy apps in the modern world rapidly. Below is the Infographic most closely associated with DevOps.
Implementing DevOps properly is definitely outside the scope of this blog but we do need to start dipping our toe in the water. We may not have fully automated testing and integration but we can certainly automate the deployment process and this will give readers an idea of how a fully automated test, deploy, and integrate process can work. To do this we will introduce two new elements - Github Actions and Laravel Envoy.
We will add a GitHub Action which will spin up a Docker container on GitHub after each code push and use it to remotely execute a command on the server which will pull our code and execute certain tasks. To execute these tasks we will require a "task runner". A task runner is a piece of kit that controls all the tasks that need to be executed when a new code is deployed to a production (or test) environment. This generally involves testing the code, using composer to pull down new packages, using npm to pull down new modules, and running any database migrations to keep the DB in synch.
It's important to remember that (due to the fact that both the vendor folder and the node_modules folder are listed in .gitignore) the code in our repo is necessarily missing all the Composer PHP packages and all the Node packages on which our application is highly dependent are missing from the repo. Until we run composer update and npm install on the remote repo they'll be missing from the production server also. Given all this, a task runner that can help us do all the tidy-up tasks involved in synchronisaiton of our systems is a highly necessary tool.
Laravel comes with its own task runner called Envoy. Envoy allows us to control what happens on other servers using PHP. It's also possible to execute these commands without Envoy simply by using a shell script but Envoy keeps all the control within Laravel. Once we have our server up and running and our DevOps setup in place we don't expect that anyone will have to login to the server and start writing shell script code. This is a cleaner solution and way more secure. To install Laravel Envoy type
php composer.phar require laravel/envoy
Do a git commit so that your changes to composer.json will be committed to the repo. The new files associated with the Laravel Envoy package will not have been added however as any new packages added to the vendor folder are ignored in .gitigore. To update the server we will need to login to the server, pull the changes, and do a composer update. Before we do that there is one more file we need to add to our Laravel Envoy task runner.
The envoy task runner is a list of commands stored in the root folder of your application in a file called Envoy.blade.php. The blade syntax is used as it is straightforward and familiar. While it is possible to arrange more elaborate setups by having multiple files and including them in Envoy.blade.php the vast majority of task runners using Envoy stick to one file. Each task is split up using an @task directive and then they are called one at a time by the @story which groups them together. The following is a fairly standard Envoy.blade.php that could be used in man Laravel setups. Firstly fresh code is pulled, any database migrations are run, a composer update is run, followed finally by a npm update and a npm build. Add the following code into an Envoy.blade.php file located in the root folder of your repository. Replace the 101.101.101.101 placeholder with the IP Address of your server.
@servers(['web' => '101.101.101.101'])
@story('deploy')
timestamp
git-pull-code
database-migrate
composer-update
node-update
npm-run-prod
@endstory
@task('timestamp', ['on' => 'web'])
echo '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>'
date
echo '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>'
@endtask
@task('git-pull-code', ['on' => 'web'])
cd `ls`
rm -f composer.lock
rm -f package-lock.json
rm -f public/css/app.css
rm -f public/js/app.js
rm -f public/mix-manifest.json
git pull origin main
@endtask
@task('database-migrate', ['on' => 'web'])
cd `ls`
php artisan migrate --force
@endtask
@task('composer-update', ['on' => 'web'])
cd `ls`
php composer.phar update --no-interaction --optimize-autoloader
@endtask
@task('node-update', ['on' => 'web'])
cd `ls`
source ~/.nvm/nvm.sh
npm install
@endtask
@task('npm-run-prod', ['on' => 'web'])
cd `ls`
source ~/.nvm/nvm.sh
npm run prod
@endtask
Then the files in the Laravel Envoy package will be pulled to the vendor folder on the server.
Now we are ready to update our code on the server. To do this connect to the server using putty and logging in as root, then login under the application account your have created. cd in the application folder ogin to the account you have set up to host your application, cd into the application folder. Now type
git pull origin main
This will update composer.json - then type
php composer.phar update
This will pull down the Laravel\Envoy package to the vendor folder and update the autoloader while also pulling the task runner file Envoy.blade.php.
In the next step we'll look at how to remote-execute the envoy command from github to run the deploy story every time we push new code.