How to integrate Platform.sh with GitLab CI

Blog Tags: Platform.sh, GitLab

This post gives a high level overview of how one would go about achieving a CI/CD workflow using GitLab and Platform.sh.

Tools and prerequisites needed

GitLab CI

Platform.sh-GitlabCI-01.png

 

GitLab provides continuous integration via its service GitLab CI (https://about.gitlab.com/gitlab-ci/). Using GitLab CI, you can construct pipelines which will typically contain multiple jobs which are executed in stages. A stage can be defined as a group of similar jobs bunched together which will be run in parallel if there are enough runners available. Stages are usually configured to run in a sequential manner, but there is the provision to configure stages such that the failure of a job in one stage won’t stop the next stage in the pipeline.  

You would ideally want to configure the pipeline such that it corresponds to your organization's development and deployment workflow. You will typically run code sniffers, automated tests, packaging and release management processes, and if everything works, deploy the source code to your integration/testing environment.

Platform.sh

Platform.sh provides hosting for many popular modern application frameworks, like Drupal and Symfony, among others. Platform.sh also has a CLI tool that provides deep integration into your project and environment. If you are using GitHub, the CLI tool provides out-of-the-box integration with GitHub via the add integrations command. However, GitLab is not yet supported by this CLI tool.

Unlike a typical hosting platform, Platform.sh’s deployment and configuration workflow is very tightly integrated with Git. Instead of locking you down with dev, stage and production environment workflows, Platform.sh provisions a separate sandboxed environment for every git branch you push.

To get started, you will need an account on Platform.sh. We used the scripts developed by Patrick Dawkins (https://github.com/pjcdawkins). These scripts will help you interact with platform.sh environments.

PRO TIP: You can connect to their Slack channel (chat.platformsh.com) for support and help—the channel is very active.

Instructions

.gitlab-ci.yml file

In order to get started with GitlabCI, you will first need to configure a .gitlab-ci.yml file in your repository. The file should be kept in the root of your repository and will contain directions for how your CI will work. You can read more about this here: https://docs.gitlab.com/ce/ci/yaml/README.html.

Let’s take a look at the example below. You would first start by defining build stages in your pipeline. Now the general syntax for this file will look something like this:

stages:

- stage1

- stage2

- stage2

job1:

stage: “stage1"

script: “some-script”

job2:

stage: “stage2"

script: “some-script”

job3:

stage: “stage3"

script: “some-script”

job1:

stage: “stage1"

script: “some-script”

job2:

variables:

 SSH_KEY: “sshkey”

stage: “stage2"

script:

 - rm keys

- some-script

job3:

stage: “stage3"

script:

 - some-script


You can include the keyword stage inside your job to indicate which stage this job belongs to.

The script can be some script you would be call or it can also be set of bash commands you want to run. Also, you can set the variables you need to pass to a job.


before_script:

- execute-some-before-script


after_script

- execute-some-after-script


Along with stages and job definitions you can also add the keywords, before_script and after_script. These are commands that you want to run before and after each job’s scripts. This can be prep work you want to do for a particular job or a cleanup job after the scripts have been executed.

For information on keywords refer to https://docs.gitlab.com/ce/ci/yaml/README.html

platform.sh scripts

We will break down our task into three scripts which will deploy a branch from GitLab to Platform.sh. We will then configure each of the scripts as a job in the gitlab-ci.yml file. The scripts below are strictly a starting point, and I would recommend not using them directly without modification. I will mention some changes I made, which you might have to make as well.

First, we set up SSH:


#!/usr/bin/env bash

set -e


# Set up SSH credentials for pushing to external Git repositories, via GitLab CI

# environment variables. You should add this public key to a Platform user for

# the push to be successful.


if [ -n "$SSH_KEY" ]; then

mkdir -p $HOME/.ssh

echo "$SSH_KEY" > $HOME/.ssh/id_rsa

echo "$SSH_KEY_PUB" > $HOME/.ssh/id_rsa.pub


# Some vague semblance of security for the private key.

chmod go-r $HOME/.ssh/id_rsa

unset SSH_KEY


echo "Created SSH key: .ssh/id_rsa"

fi


# Set up SSH known hosts file.

if [ -n "$SSH_KNOWN_HOSTS" ]; then

mkdir -p $HOME/.ssh

echo "$SSH_KNOWN_HOSTS" > $HOME/.ssh/known_hosts

fi


Here, I had to add replace the known hosts section:

ssh-keyscan git.us.platform.sh >> $HOME/.ssh/known_hosts

This made sure that the platform host key was always present in my known_hosts. Replace git.us.platform.sh with your region.

Then we make sure we have the platform CLI tool installed on our CI server. You can add this to your CI server’s Vagrant/Docker and remove it from the script.


#!/usr/bin/env bash

set -e

VERSION="3.12.0"

SHA1="537c8ce57c06f19fe1bbd0e5ca9712edb03c82e9"

# Install the Platform.sh CLI.

if [ ! -f /usr/local/bin/platform ] || [[ ! "$(platform --version)" == *"$VERSION" ]]; then

echo "Downloading the Platform.sh CLI version $VERSION"

curl -sfSL -o platform.phar https://github.com/platformsh/platformsh-cli/releases/download/v"$VERSION"/platform.phar

if [[ "$(shasum platform.phar 2>/dev/null)" != "$SHA1"* ]]; then

  echo "SHA1 checksum mismatch for platform.phar"

  exit 1

fi

chmod +x platform.phar

mv platform.phar /usr/local/bin/platform

fi

You might want to modify this script a bit depending on how you want to install the CLI. If you don’t have sudo permissions for your runner, you might want to keep the .phar file in a directory other than /user/local/bin. Also, as you can see, we are downloading a specific version. Modify the versions and SHA1 sum to the latest build available.

Finally, we have a script to deploy to Platform.sh. In order to run this script successfully you will need to create an API token which can be exchanged for authentication. In order to generate the API token, login to your Platform.sh account and go to the Account Settings tab. Under the left vertical menu, you should see the API Token tab.

Platform.sh and GitLab for CI

 

Generate an API token and then copy it into a file under $HOME/.platformsh/apitokenfile.


touch $HOME/.platformsh/apitokenfile

echo “<token>” >> $HOME/.platformsh/apitokenfile


Then create a config.yml file in the .platformsh directory.


touch $HOME/.platformsh/config.yml

vim $HOME/.platformsh/config.yml

 

Then add the lines below to this file and save.

api:

token_file: <path to token file>


Once the steps above are done correctly, the following script should work as is without any changes.

#!/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.

# Config.

PF_PROJECT_ID=${PF_PROJECT_ID:-"abcdefghijklm"}

PF_PARENT_ENV=${PF_PARENT_ENV:-"master"}

ALLOW_MASTER=${ALLOW_MASTER:-0}

CLI_CMD=${CLI_CMD:-platform}

if [ -z "$CI_BUILD_REF_NAME" ]; then

echo "Source branch (CI_BUILD_REF_NAME) not defined."

exit 1

fi

SOURCE_BRANCH="$CI_BUILD_REF_NAME"

PF_BRANCH=${PF_DEST_BRANCH:-$SOURCE_BRANCH}

# This script is not for production deployments.

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

echo "Not pushing master branch."

exit

fi

PF_BRANCH_ENCODED=$(php -r "echo urlencode('${PF_BRANCH}');")

PF_PROJECT_UI=$($CLI_CMD web --project="$PF_PROJECT_ID" --pipe)

echo ""

echo "Web UI: ${PF_PROJECT_UI}/environments/${PF_BRANCH_ENCODED}"

echo ""

# Push to PS.

$CLI_CMD project:set-remote "$PF_PROJECT_ID"

COMMAND="$CLI_CMD push --force --target=${PF_BRANCH}"

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

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

fi

$COMMAND

# Clean up already merged and inactive environments.

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

It should push the branch to your platform project as a new environment. The script also has instructions to delete any inactive environments as well as environments that are already merged with master. If you don’t want this to happen, just change the last part of the script.

Once you have successfully executed the last script without any errors, you should see a new environment provisioned in your platform.sh project. Cheers!