SnapShooter Backups Server, Database, Application and Laravel Backups - Get fully protected with SnapShooter

Deployment script in PHP

Deployment strategy varies depending on your project's need. For small projects, we lean towards using Git deployment strategy, where a git pull is triggered whenever a git push is issued in certain branch. This strategy is simply and easy to setup. It solves most of use cases for small projects.

However when a build process is required or you do not have Git installed in your production server at all. You need an alternative. Normally this is when we start writing Bash scripts. If you are like me, don’t have a lot experience with bash scripting, yet still want to a deployment strategy similar to running a bash script. This tutorial is for you.

In this tutorial, we will introduce a way of writing a Bash script alike deployment script using the combination of PHP and Bash script.

The overall idea is that, we will use PHP to handle tasks like scanning directories, and use simple bash commands such as rm, scp, mkdir and so on for command tasks.

Everything is done from our local development machine. We will use ssh to do things that is required in remote server. That gives us convenience of doing everything in one place, no jumping between local and production server.

The overall deployment process is: we will clone source code from a repository, copy over distribution files which are normally ignored from the repository, compress full source code into a zip file, transfer it to the production server, then run a series of commands in production server via SSH.

In this tutorial, we assume your production server is running on Nginx and your website root directory is at /var/www/your-site. However you should be able to adapt this idea and change it to whatever environment easily.

Configuration file - prop.php

We had the need to move images from our legacy server to a new server for one of our projects. Instead of downloading hundreds of images manually, which was too tedious. We used Flysystem to do the job for us.

We will store them in a php file. Create a prop.php file with following variables. You will have to change their values to your own:

$gitRepo = 'git@github.com:xxx/xxx.git';
 
$sshUser = 'deploy';
 
$sshHost = 'x.x.x.x';
 
$remoteSiteDir = '/var/www/my-site.com';
  • $gitRepo: Git repository address. Use HTTPS/Git depending on your own need. Both of them should work fine.
  • $sshUser: SSH username. You should already configure SSH login to your production site.
  • $sshHost: SSH hostname. It is normally an IP address, however you can also use any arbitrary name if you have configured it in ~/.ssh/config file.
  • $remoteSiteDir: This is the directory in production server, where you have configured Nginx to treat it as your website's root directory.

What does this mean is that we can treat all the filesystems above as if they are the same. The benefits are obvious. We can switch between the filesystems without much of trouble. And we can move a file from one filesystem to another in a breeze.

Generic functions - func.php

Next we need some generic functions in deployment script.

Functions we need are:

  • Recursively copy content of a directory to another directory.
  • Copy a file to a directory.
  • Remove a directory completely.

Create a func.php file with code as shown below:

function recursiveCopyDir($srcDir, $destDir)
{
    foreach (new DirectoryIterator($srcDir) as $fileInfo) {
        if ($fileInfo->isDot()) {
            continue;
        }
 
        if (!file_exists($destDir)) {
           shell_exec('mkdir -p '.$destDir);
        }
 
        $copyTo = $destDir . '/' . $fileInfo->getFilename();
 
        copy($fileInfo->getRealPath(), $copyTo);
    }
}
 
function copyFileToDir($src, $desDir)
{
    if (!file_exists($desDir)) {
        shell_exec('mkdir -p '.$desDir);
    }
 
    $fileInfo = new SplFileInfo($src);
 
    $copyTo = $desDir . '/' . $fileInfo->getFilename();
 
    copy($fileInfo->getRealPath(), $copyTo);
}
 
function unlinkDir($dir)
{
    shell_exec('rm -rf '.$dir);
}

There functions are created with different purposes:

  • recursiveCopyDir: It copies files of a given directory and paste them to destination directory. We are using PHP's DirectoryIterator class for the task of scanning a directory, which is easy and straightforward comparing to bash script for a bash novice.
  • copyFileToDir : Copy a file to a directory. We create a directory if it does not exist. This task is easier if we use bash command (mkdir -p), so we can use PHP's shell_exec function to execute bash command directly.
  • unlinkDir: Remove a directory recursively. Again this task can be done easier using bash command.

As you can see, we are using a combination of PHP and Bash. By taking advantage of the best of the two languages, we came out a set of powerful auxiliary functions.

Template files - template

Template files are configuration files that you do not commit to your Git repository. But yet your application relies on them to run. Things like database configuration file, email configuration file as well as any third party configuration files.

We will place them in a folder called template. Depending on your application design, you will have variety of configuration files. For this example, let us assume we have only one database.php for simplicity.

Deployment script - deploy.php

Here comes the meat of this tutorial.

Create a deploy.php, this is the deployment script, we are going to use when deploying our application to production server.

First we need to include prop.php and func.php to the deployment script. Meanwhile we will deploy a folder with current time stamp to production server, we should create the folder name first.

Beginning of the deployment script looks like this:

require './config/prop.php';
require './lib/func.php';
 
$release = time() . '-' . date('Ymd');
$releaseDir = 'release/' . $release;

Next we will clone the git repository to the release folder:

echo 'Clone package... '.PHP_EOL;
shell_exec(sprintf('git clone %s %s', $gitRepo, $releaseDir));

Then we will copy over whatever template files. For demonstration purpose, we will assume we need some sort of config files in app/Config folder. So we would do something as shown below:

echo 'Copy config files... '.PHP_EOL;
recursiveCopyDir('template', ($releaseDir . '/app/Config'))

You may only have a single config file. In this case you can use the copyFileToDir function:

echo 'Clone assets.config.json... '.PHP_EOL;
copyFileToDir($assetsConfigJson, ($releaseDir . '/app/tmp'));

We do not want to deploy the git repository to production server. Let's remove it:

echo 'Remove .git files... '.PHP_EOL;
unlinkDir($releaseDir . '/.git');

Next we will prepare a zip file before sending the source code to production server:

echo` `'Create zip... '``.PHP_EOL;
shell_exec(sprintf(``'zip -r -X %s.zip %s'``, ``$releaseDir``, ``$releaseDir``));

Then we will upload the zip file to production server via SCP. You will certainly need to config the SSH access first for this to work. Since that's not the focus of this tutorial, we skipped that.

echo 'Upload to server... '.PHP_EOL;
shell_exec(sprintf('scp %s.zip %s@%s:%s', $releaseDir, $sshUser, $sshHost, $remoteSiteDir));

After uploading the zip file to production, we should tidy up by removing the local copies:

echo 'Remove release files... '.PHP_EOL;
shell_exec('rm -rf '.$releaseDir);
shell_exec('rm -rf '.$releaseDir.'.zip');

In final step, we will run a series of bash commands in production server via SSH:

echo 'Activate release... '.PHP_EOL;
$command = 'cd '.$remoteSiteDir.';' . 'unzip '.$release.'.zip;' . 'chmod 775 -R '.$releaseDir.'; '. 'rm '.$release.'.zip; ' . 'ln -nfs '.$remoteSiteDir.'/'.$releaseDir.' '.$remoteSiteDir.'/public_html;'. '/etc/init.d/nginx reload;'; shell_exec(sprintf('ssh %s@%s "%s"', $sshUser, $sshHost, $command));

You must be curious what these bash commands are doing actually. Here is a list of actions they perform:

  • Navigate to the website root directory.
  • Unzip uploaded zip file to release directory.
  • Change the release directory permission to 775 (full permission for owner and group user and executable by the world).
  • Remove uploaded zip file.
  • Create soft link between release directory and website public directory.
  • Restart Nginx server, so the release directory will be live.

The End

Hope you have enjoyed this tutorial and learned something useful for your next project. If you have any questions or you have some suggestions for us, do leave a comment below. Lastly sharing is caring, please share this tutorial to help it reach more audiences.

Hopefully this simple tutorial helped you with your development . If you like our post, please follow us on Twitter and help spread the word . We need your support to continue. If you have questions or find our mistakes in above tutorial, do leave a comment below to let us know .