CI/CD of CakePHP with Bitbucket Pipelines & shared hosting server via FTP
By: Kai Niklas
Tags: Bitbucket, Web Development, Cakephp, Continuous Integration, Continous Deployment

Prerequisites

Bitbucket account

Git installed on development machine

PHP installed on your development machine

Composer installed on development machine

Create a Bitbucket repository

First we create a new Bitbucket repository.

Create new Bitbucket repository

Initialize and commit CakePHP application

For this demonstration we create a skeleton CakePHP application by using the following command which we type into our command line:

composer create-project --prefer-dist cakephp/app cakephp-ci-demo

If you are interested in what you just created start it with the build in server and start your favorite browser:

cd cakephp-ci-demo

bin/cake server -p 8765

Demo app in browser

Now we can share (aka link) the local git repository with our Bitbucket repository. You can find your git URL in the Bitbucket repository overview. For me the commands look like this:

# switch to porject folder in ./cakephp-ci-demo

git remote add origin git initgit remote add origin https://kainiklas@bitbucket.org/kainiklas/cakephp-ci-demo.git git add -A

git commit -m "Initial commit"

git push origin master

Now the code should be visible in our Bitbucket repository.

Initialize Bitbucket Pipeline

We navigate to our repository on bitbucket.org and click on Pipelines in the left navigation menu. The first time we click on it, it will take some time until we see something (~ 5–10 seconds). We scroll down the page and choose PHP. Then we commit the file and the pipeline starts automatically.

At first, it looks all nice and pretty, but then the pipeline fails with some problems which we will fix later.

#1: cakephp/cakephp 3.5.11 requires ext-intl * -> the requested PHP extension intl is missing from your system. #2: symfony/debug v4.0.3 requires php ^7.1.3 -> your PHP version (7.1.1) does not satisfy that requirement. And 5 more.

First Pipeline run fails

Deployment strategy via FTP using PHPloy

We want to deploy our code to the shared hosting server via FTP. This can be achieved in several ways. A pretty general way is for example the tool git-ftp. But we will use PHPloy which I prefer as it has some handy additional features. Most important for me are the following:

Native PHP support as we have a PHP project A configuration file phploy.ini to define several staging environments. Transfer files which are ignored by git, e.g. the /vendor folder which is required to run the app. Ignore file which are versioned in git, e.g. the /test folder which is not required on a production server. Remove files on the server, e.g., clean /cache folder.

Install and configure PHPloy for production deployment

First, we require the PHPloy dependency using composer:

# switch to ./cakephp-ci-demo

composer require "banago/phploy"

Then we create the phploy.ini file with the following content for the production environment. Do not forget to change user and host with your values. The password we set later as an environment variable.

Complement the composer.json descriptor

We want to have a deploy command which we can call via composer. This can be achieved easily by adding the following line in the composer.json file in the scripts section:

"scripts": {

... "deploy-to-production": " phploy -s production "

}

Before we deploy anything into production we want to run all unit tests to make sure that everything works. Therefore, we need to include it in our composer file:

composer require --dev phpunit/phpunit

Configure Bitbucket Pipeline for CakePHP

Our goal is to push updated code to a shared hosting provider via FTP if the code builds and all tests pass. Therefore, we first need to store (securely) the credentials to our build server (the pipeline in Bitbucket). We will do this using environment variables instead of hard coded values in the build script.

Set the password environment variable

The environment variable we set is called PHPLOY_PASS and contains the password of the FTP user.

Important: Never store passwords in any code repository.

Configure the pipeline

Lets check out the pipeline file which we generated earlier in Bitbucket and configure it for CakePHP:

# switch to ./cakephp-ci-demo git pull origin master

Open the file bitbucket-pipelines.yml to configure it.

Explanation of the configured pipeline

With image: edbizarro/bitbucket-pipelines-php7 we tell our piepline which docker container should be used for the build. For CakePHP we require a higher PHP 7 version as the default php image provides. Further, we would need to install composer and the php extension intl using the default PHP 7 container. Otherwise our build will fail as we already saw earlier. The docker image edbizarro/bitbucket-pipelines-php7 contains the tool and the php extension (and more which we do not need for the demo).

Using branches: master: we define, that the pipeline should run as soon as we check in code to the master branch.

With caches: -composer we cache all downloaded composer packages which are downloaded during the installation of dependencies. This will speed up the subsequent runs.

The last part are the scripts in the script: section. We 1) install all required modules, 2) test the application and 3) deploy it to the server. If any command fails, the whole pipeline fails. That means, a failed test will prevent from deploying to the server (which is good as we do not want to break the production).

Test the Pipeline

Testing our pipeline is pretty easy. We simply need to change a file and push it to the master branch. As we have not checked in any file into our repository yet, we can test the pipeline by checking in our work now:

# switch to ./cakephp-ci-demo git add -A

git push origin master

Final thoughts

The presented pipeline is a simple demonstration and can be enhanced in several ways. Topics for a follow up include:

Deployment to multiple stages (DEV, QA, PROD)

Propagation of stable code using different git branches

Execution of tests including a DB

DB migration tests

Rollbacks

Further readings