Currently, there are multiple services for continuous integration and continuous deployment (CI&CD). Though, if you use Atlassian products, you might have heard about the Bitbucket service called Pipelines. It enables continuous delivery in Bitbucket Cloud repositories for projects using any programming languages, such as Java, Node.js, PHP, Python, Ruby, etc.
Documentation of the service describes various cases. This is a universal technology. However, if you are working on a specific solution, it will require more efforts.
To better understand how Bitbucket Pipelines works for Laravel, let’s focus on an example situation. So, we have:
- An application written with Laravel;
- Tests that should be launched for every Push;
- Remote server with SSH access, where the application is deployed to.
And the work begins!
Enable Pipelines
Go to your repository settings and tick “Enable Pipelines”.
This will work only if you are an admin in your repository. If not - contact your admin and request enabling Pipelines. You won’t need admin access rights for any further actions.
Bitbucket-pipelines.yml
The admin panel will immediately suggest you to create a bitbucket-pipelines.yml file. You can agree, or create the file in the root of the project and add it to the next commit.
According to its extension, the file will have YML syntax. So, you might want to enable support of it in your code editor or IDE.
Your YML file should look like this:
pipelines:
default:
- step:
script:
- echo "It's Clockwise, baby!"
Usually, it also includes a link to an container image the commands will be executed in, and a set of operations, bash commands, which should be executed in this container.
As soon as you make push in the repository, Pipelines will launch. According to our mentioned example, it will display the phrase “It’s Clockwise, baby!”.
In the repository in Pipelines section you will see something like this:
After that, every push will initiate execution of commands from this file.
Getting closer to PHP
To work with PHP, we have to add image: directive with the required value to our pipelines. Since we are talking about a Laravel project, we’ll need a PHP Docker image. The documentation suggests using the basic official image. The configuration file will look as follows:
image: php:7.1.1
pipelines:
default:
- step:
script:
- php -v
However, this is still not enough.
The project will also require you to install a variety of PHP modules and composer packages including Laravel itself. Further, there will be several options:
- Describe bitbucket-pipelines.yml to include all needed dependencies;
- Create a custom image including all required components and reuse it;
- Search Docker Hub for the appropriate image.
Docker image
For our example project we create a custom image. It includes the needed dependencies and uses different systems and different PHP variations, depending on the tag. This image can be used for development or production environment (php-fpm), as well as with various CI&CD systems (cli).
Now, we can launch commands in the container which includes the whole code of our repository. This is how the bitbucket-pipelines.yml file looks like:
image: clockwise/docker-phpunit-alpain:master
pipelines:
default:
- step:
caches:
- composer
script:
- composer --version
# install composer vendor scripts
- composer install
- vendor/bin/phpunit --version
# generate key
- cp .env.example .env
- php artisan key:generate
# do not migrate - it's happens in test environment setup
# - php artisan migrate --seed --database=sqlite
# run tests
- vendor/bin/phpunit
The overall concept is quite simple. However, the key points need some deeper explanation.
In the first string, directive image: refers to container image, which will be used for all further operations. We also use tag: master which derives from master branch of the repository, including alpine version of the official php repository from Docker Hub.
The general pipelines behavior for each of the repository branches is as follows:
- caches: describes one of the new Bitbucket Pipelines features. It enables caching the deployment of the vendor part of the project, which results in faster deployment in pipelines.
- script: includes the list of commands to be executed. It resembles deployment of the project, which was just cloned from the repository.
- composer --version ensures that the container includes a composer and displays its version.
- composer install installs all the vendor dependencies;
- vendor/bin/phpunit --version checks the availability of phpunit in the vendor directory;
- cp .env.example .env copies the default environment config;
- php artisan key:generate generates the application key;
- php artisan migrate --seed --database=sqlite - this command is commented in the example configuration, since we are using SQLite in Memory for the project. If you use SQLite database for testing, this command is required;
- vendor/bin/phpunit - the command that runs the tests.
If one of the tests fails, you will be notified via email. The repository history will display a red exclamation mark for your commit.
The pipelines history will highlight the failed build. You will see the full conclusion, as if you were running a test in your console.
If one of the commands fails during the execution, the process will stop on the command that couldn’t be executed successfully.
Enabling in-memory SQLite in a Laravel application
SQLite doesn’t require creating an extra file to store the database. Instead, you can store it directly in the system memory, which is extremely handy for testing. To enable this, you will have to modify the Laravel application.
Start with modifying the config/database.php file by extending the connections section with the following strings:
'sqlite_memory' => [
'driver' => 'sqlite',
'database' => ':memory:',
'prefix' => '',
]
This enables using sqlite_memory as a database connection parameter.
Also, you will have to update the phpunit.xml file, responsible for default PHPUnit configuration, in the root of your application. Add the following:
<env name="DB_CONNECTION" value="sqlite_memory"/>
The last, but not least. The application should apply migrations and seeds on the database. So, you’ll have to update the tests/CreateApplication.php file by adding setUp method redefinition:
public function setUp()
{
parent::setUp();
\Artisan::call('migrate');
\Artisan::call('db:seed');
}
These simple actions enable you to add or remove files from SQLite database without any issues.
One more thing
The commands will be executed for every push in the repository. In addition, the same will happen for pull-requests as well. This is very helpful for the whole team, however, with a single condition: you should cover your own code with tests.