Github Actions: Continuous Deployments for PHP Applications

For the last couple of years, I have been working with Ruby-based Capistrano to deploy my projects to staging and production environments. In my experience the tool isn't really hassle-free but the minor problems and/or hickups are usually solvable. In the end

For the last couple of years, I have been working with Ruby-based Capistrano to deploy my projects to staging and production environments. In my experience the tool isn't really hassle-free but the minor problems and/or hickups are usually solvable. In the end it is a very helpful tool, since it gives the developer the freedom to decide when to deploy what branch to which environment by typing a one-liner into the console –  namely cap production deploy. Goodbye Capistrano. Last year GitHub launched a new feature called GitHub actions which allows users to automate tests, code validations and so on, based on triggers or events caused by interacting with the repository. One can even use pre-made actions, shared by the community. Our first Action Let's create our first GitHub action. We create a new file .github/workflows/deploy.yml and save it to the folder .github/wokflows in our repository.
name: Deploy Action

on: 
   push:
      branches:
         - master
jobs:
  deploy:
     name: Deploy
     runs-on: ubuntu-latest
     steps:
       - uses: actions/[email protected]
       - name: Zip entire project
         uses: montudor/[email protected]
         with:
             args: zip -qq -r ./latest.zip .
       - name: Send zip to remote server
         uses: horochx/[email protected]
         with:
           local: "./latest.zip"
           remote: "/var/www/html"
           host: ${{ secrets.HOST }}
           port: "22"
           user: "root"
           key: ${{ secrets.KEY }}

       - name: Execute post-deploy.sh
         uses: appleboy/[email protected]
         with:
           host: ${{ secrets.HOST }}
           username: "root"
           key: ${{ secrets.KEY_RAW }} 
           port: "22"
           script: sh ~/post-deploy.sh
  1. The first bit, defines that this action only runs when we push changes into our master branch.
  2. Then we have the actual job, defining we need to do. Starting with a checkout of the projects' code (the contents of our repository).
  3. Now we zip everything into latest.zip
  4. After we have our project zipped we scp the file over to the remote production server.
  5. And finally we execute a file called `post-deploy.sh` on our remote server. There we will do the actual unpacking of the zip file, set permissions, symlink asset folders and finally reload our server. So how does this post-deploy script look like.
#!/bin/sh

# create release folder
datestring=$(date +"%Y-%m-%d-%H-%M")
cd /var/www/html/releases
mkdir $datestring

# unzip to release
unzip -o -qq /var/www/html/latest.zip -d /var/www/html/releases/$datestring 

rm -rf /var/www/html/releases/$datestring/wordpress/wp-content/uploads 
ln -s /mnt/volume_fra1_01/shared/wordpress/wp-content/uploads /var/www/html/releases/$datestring/wordpress/wp-content/uploads 

rm /var/www/html/releases/$datestring/.env
ln -s /mnt/volume_fra1_01/shared/.env /var/www/html/releases/$datestring/.env

# symlinks
rm /var/www/html/current ln -s /var/www/html/releases/$datestring /var/www/html/current
# finally refresh nginx
service nginx reload
Similar to Capistrano's deploy logic I want to keep the last n releases in order to being able to revert a new commit easily. So we create a releases folder where we