Using Platform.sh for Static Code Analysis & Regression Testing

Tags: devops


Platform.sh is a PaaS platform with support for deployments using git push. The platform currently provides support for PHP, NodeJS, Python and RoR applications (beta), along with associated services like search (Solr, Elasticsearch), databases (Postgres, MySQL, MongoDB), queuing (RabbitMQ) and caching (Redis). The platform is an excellent choice for setting up a development, staging and production environment very quickly and having a robust workflow set up for continuous integration and delivery.

At Axelerant, we are big fans of automation, and in this article, we will see how we can use Platform.sh along with GitLab, Pronto and Behat to set up a workflow wherein the pushed code will go through static code analysis, followed by regression testing.

Our project for this article is going to be a PHP Slim application called HelloUser. The objective of this project is to build a small web app which responds to the user with ‘Hello’ along with their name.

Let’s start off by setting up GitLab.

Create a new project called HelloUser in GitLab.

image1-45.png

As soon as we click on the ‘Create project’ button, we’ll have our new project ready to be used.

image3-2.png

Copy the Git URL and start your favorite command line tool (I used Mac OS X and terminal for this purpose).

Create a directory called hellouser and move into it. This is where we’ll be storing our code base.

To be able to sync everything we do locally on our workstation with our GitLab server, we’ll need to set up Git. Follow these steps to complete the setup:

Initialize a Git repository      - git init

Add a remote called gitlab  - git remote add gitlab

git@code.projspace.com:mayank/hellouser.git

Now let’s go ahead and set up Platform.sh.

Log in to Platform.sh by visiting https://accounts.platform.sh and choose to create a new platform.

The following screenshots describe the actions you’ll need to perform in order to get a new project set up on Platform.sh:

image2-1.jpg

Select the ‘Add A Platform’ link.

image5.jpg

Choose the appropriate subscription plan. We are going to use the Development plan offered by Platform.sh. More details about subscription plans and pricing can be found at https://platform.sh/pricing.

image4.jpg

Choose the region where you want your infrastructure to be provisioned. This can have an impact on latency, so choose the region closest to your users.

image7.jpg

Specify the name of the project. In our case we are using ‘HelloUser’ as the name of the project.

image6.jpg

Since we have a code repository already set up for the project, we are going to choose the ‘Import your existing code’ option.

image9.jpg

Click on the ‘Continue Later’ link and go to the Account Setting section to generate an API token:

image8.jpg

Also set up your SSH Keys right now to allow access to Platform.sh servers using SSH.

image12.jpg

We’ll need this API token and SSH key pair later on while configuring the setup to do a push to Platform.sh.

Before we start off coding our application, we’ll need to add a couple of environment variables to our project in GitLab.

1. DOCKER_AUTH_CONFIG – This variable contains the Docker authorization configuration to allow the CI jobs to pull Docker images from private repositories. Here is a sample value which can be provided against this key:

{

"auths": {

"https://index.docker.io/v1/": {

"auth": "XXXX"

}

}

}

Look into ~/.docker/config.json on your machine to know what values to use for this key.

2. PLATFORM_PROJECT_ID – This variable contains the project id of the project we had setup on Platform.sh.

Let’s get started with coding our application.

Fire up your favorite editor and create a file called composer.json in the hellouser directory which we had created earlier.

{

 "require": {

   "slim/slim": "3.*"

 }

}

Create a .gitignore file

vendor/*

Finally let’s write our application code in a file called index.php

<?php

 require __DIR__ . "/vendor/autoload.php";

 

 $app = new Slim\App;

 

 $app->get('/', function($req, $resp) {

   $resp->getBody()->write(“Welcome to HelloUser app. Try /YOUR-NAME!”);

   return $resp;

 });

 

 $app->get(‘/{name}, function($req, $resp) {

   $name = $req->getAttribute(‘name’);

   $resp->getBody()->write(“Hello, $name”);

   return $resp;

 }

 $app->run();

With our code in place, let’s focus on writing our behat test case. For more information about test driven development you might want to check articles with the QA tag at www.axelerant.com/team-blog.

Create a folder called behat and create the following files:

1. behat/behat.yml

#behat.yml

default:

 autoload:

   '': %paths.base%/features/bootstrap

 suites:

   default:

     contexts:

       - FeatureContext

     paths:

       - %paths.base%/features

 extensions:

   Behat\MinkExtension:

       browser_name: 'phantomjs'

       goutte: ~

       javascript_session: selenium2

       selenium2:

         wd_host: http://127.0.0.1:4444/wd/hub

       base_url: ENV_BASE_URL

2. behat/features/hellouser.feature

Feature: Sample test

 

 Background:

   Given I am on the homepage

 

 Scenario: Test home page

   Then I should see "Welcome"

 

 Scenario: Test a user page

   When I am on "/avni"

   Then I should see "Hello, foo"

 

3. behat/features/bootstrap/FeatureContext.php

<?php

 

use Behat\Behat\Context\Context;

use Behat\Behat\Context\SnippetAcceptingContext;

use Behat\Gherkin\Node\PyStringNode;

use Behat\Gherkin\Node\TableNode;

use Behat\MinkExtension\Context\MinkContext;

 

/**

* Defines application features from the specific context.

*/

class FeatureContext extends MinkContext implements Context, SnippetAcceptingContext

 

{

   /**

    * Initializes context.

    *

    * Every scenario gets its own context instance.

    * You can also pass arbitrary arguments to the

    * context constructor through behat.yml.

    */

   public function __construct()

   {

   }

}

4. Edit composer.json and add the following code segment to it:

"require-dev" : {

   "behat/behat" : "3.0.*",

   "behat/mink-goutte-driver" : "*",

   "behat/mink-browserkit-driver" : "*",

   "behat/mink-extension" : "2.*",

   "behat/mink-selenium2-driver" : "*",

   "behat/mink" : "*"

 },

 "config": {

   "bin-dir": "bin/"

 }

5. execute-behat.sh

#!/bin/bash

 

export ENV_BASE_URL=`cat scripts/platform.url`

echo $ENV_BASE_URL

echo $CI_PROJECT_NAME

echo "---Setup behat---"

composer install

bin/behat --init

cp -r behat/features/* features; cp behat/behat.yml behat.yml

sed -i 's@ENV_BASE_URL@'"$ENV_BASE_URL"'@' behat.yml

sed -i 's@ENV_PROJECT@'"$CI_PROJECT_NAME"'@' behat.yml

echo "---Starting behat---"

bin/behat features/${CI_PROJECT_NAME}.feature

 

Now it’s time to integrate everything.

 

Create a file called scripts/push-platform.sh

#!/usr/bin/env bash

set -e

 

# This script can be configured by specifying different environment variables in

# your .gitlab-ci.yml file's invocation of the script. If those are omitted, as

# in this example, the defaults below and throughout the script should be used.

 

# Check basic requirements from Config.

if [ -z "$PLATFORM_PROJECT_ID" ]; then

 echo "PLATFORM_PROJECT_ID is required, please contact support if you don't know how to do it."

 exit 1

fi

 

# By default we use master as the Platform parent env.

PF_PARENT_ENV=${PF_PARENT_ENV:-master}

 

# By default we don't allow master to be deployed.

ALLOW_MASTER=${ALLOW_MASTER:-0}

 

# Prepare the variables.

PF_BRANCH=${PF_DEST_BRANCH:-$CI_BUILD_REF_NAME}

 

# Platform command path.

CLI_CMD=${CLI_CMD:-"$HOME/.platformsh/bin/platform --yes"}

 

if [ -z "$PF_BRANCH" ]; then

 echo "Source branch (CI_BUILD_REF_NAME or PF_DEST_BRANCH) not defined."

 exit 1

fi

 

# This script is not for production deployments.

if [ "$PF_BRANCH" = "master" ] && [ "$ALLOW_MASTER" != 1 ]; then

 echo "Not pushing master branch."

 exit

fi

 

# Set the project for further CLI commands.

COMMAND_SET_REMOTE="${CLI_CMD} project:set-remote ${PLATFORM_PROJECT_ID}"

eval $COMMAND_SET_REMOTE

 

# Push to PS.

COMMAND_PUSH="${CLI_CMD} push --verbose --force --target=${PF_BRANCH}"

if [ "$PF_PARENT_ENV" != "$PF_BRANCH" ]; then

 COMMAND_PUSH="$COMMAND_PUSH --activate --parent=${PF_PARENT_ENV}"

fi

eval $COMMAND_PUSH

 

# Clean up already merged and inactive environments.

COMMAND_CLEANUP="${CLI_CMD} environment:delete  --verbose --inactive --merged --environment=${PF_PARENT_ENV} --exclude=master --exclude="${PF_BRANCH}" --yes --delete-branch --no-wait || true"

eval $COMMAND_CLEANUP

 

# Store url of new environment in a file

${CLI_CMD} environment:url -e $CI_BUILD_REF_NAME --pipe | head -1 > scripts/platform.url

 

Let’s create a .gitlab-ci.yml file.

 

stages:

- push-to-platform

- execute-behat

 

variables:

 DOCKER_DRIVER: overlay

services:

- docker:dind

 

deploy-hellouser:

 image: maxc0d3r/platformshcli:maxc0d3r

 variables:

   PLATFORMSH_CLI_UPDATES_CHECK: '0'

   PF_PARENT_ENV: "master"

   ALLOW_MASTER: '1'

 stage: push-to-platform

 script:

 - chmod +x ./scripts/push-to-platform.sh

 - script -q -e -c "./scripts/push-to-platform.sh"

 artifacts:

   paths:

   - scripts/platform.url

   expire_in: 1 week

 tags:

   - autoscaler

 

test-hellouser:

 image: maxc0d3r/behat

 stage: execute-behat

 script:

 - echo "---Starting behat---"

 - ls -lR

 - chmod +x ./behat/execute-behat.sh

 - script -q -e -c "./behat/execute-behat.sh"

 tags:

   - autoscaler

 

We are making use of two Docker images for our jobs:

maxc0d3r/platformshcli

This is a private Docker image which can be usedto push code to Platform.sh. The code used to build this Docker image is available at https://github.com/axelerant/docker-platformshcli. Fork the repository, modify the code as described in the README and create your own Docker image for use. You’ll need an API token and the SSH Key pair which we’d generated earlier while setting up the project on Platform.sh. We are also using the autoscaling feature of GitLab, and the runners are tagged as autoscaler. We’ll cover more on how to set up autoscaling of GitLab runners in another post.

maxc0d3r/behat

This is a publicly available Docker image with dependencies required for the installation of behat. The code used to build this Docker image is available at https://github.com/axelerant/docker-behat.

We are generating an artifact in the deploy-hellouser job called scripts/platform.url. This file will contain the URL of the application when it’s deployed to Platform.sh. We will be using this file to know the URL in test-hellouser job and configure behat to test against this URL.

Let’s push our code, and voila, we can see the pipelines in action.

 image10.jpg

So our pipeline failed in the second stage, that is test-hellouser. Let’s look at our job details to see the reason for failure: image11.jpg

Great! So our test failed because it was meant to fail. Let’s push a change into our feature file (behat/features/hellouser.feature):

Feature: Sample test

 

 Background:

   Given I am on the homepage

 

 Scenario: Test home page

   Then I should see "Welcome"

 

 Scenario: Test a user page

   When I am on "/avni"

   Then I should see "Hello, avni"

 

Let’s look back at our pipelines:

 image13.jpg

Cool! So we have our test cases passing now. We’ll be adding more features into our setup, like static code analysis, in the future. Keep visiting our team blog to know what’s cooking. 

Mayank Joshi
Posted by

Mayank Joshi

Guest Blogger