This is the second in a series of articles involving the writing and launching of my DurableDrupal Lean ebook series website on platform.sh. Since it’s a real world application, this article is for real world website and web application developers. If you are starting from scratch but enthusiastic and willing to learn, that means you too. I’m fortunate enough to have their sponsorship and full technical support, so everything in the article has been tested out on the platform. A link will be edited in here as soon as it goes live.
Diving in
Diving right in I setup a Trello Kanban Board for Project Inception as follows:

Both Vision (Process, Product) and Candidate Architecture (Process, Product) jobs have been completed, and have been moved to the MVP 1 column. We know what we want to do, and we’re doing it with Drupal 7, based on some initial configuration as a starting point (expressed both as an install profile and a drush configuration script). At this point there are three jobs in the To Do column, constituting the remaining preparation for the Team Product Kickoff. And two of them (setup for continuous integration and continuous delivery) are about to be made much easier by virtue of using platform.sh, not only as a home for the production instance, but as a central point of organization for the entire development and deployment process.
Beginning Continuous Integration Workflow
What we’ll be doing in this article:
- Overcoming the confusion between Continuous Integration and Continuous Delivery
- Create Ansible playbook to create local development VM suitable for working with platform.sh
- Clone the playbook from GitHub into a workspace on local dev box and vagrant up
- ssh into the local dev box, log into platform.sh and manage ssh keys
- Get the project and sync the platform server and local databases
- Configure local dev virtual host and bring up website
- Exercise CI workflow by doing an upgrade and pushing via repo to platform
Overcoming the confusion between Continuous Integration (team development with a codebase) and Continuous Delivery (deploying to an environment).
“So what is CI? In short, it is an integration of code into a known or working code base…. The top benefits are to provide fast feed back to the members of the team and to ensure any new changes don’t break the working branch.”
“CD… is an automated process to deliver a software package to an environment…. we can now extend the fast feedback loops and reduction of constraints with packaging techniques, automation workflows, and integrated tools that keep track of the software versions in different environments.”
“CI and CD are two completely separated practices that are tightly interlocked to create a unified ALM [Application Lifecycle Management] workflow.” – Bryan Root
And let’s take it step by step by breaking the CI Workflow Job into tasks:

Create Ansible playbook to create local development VM suitable for working with platform.sh
Based on Jeff Geerling’s great work both on Ansible itself as well as its use with Drupal provisioning, I whipped up and tested ansible-vm-platformsh. Dependencies:
Clone the playbook from GitHub into a workspace on local dev box and vagrant up
With the dependencies installed, and once the ubuntu/trusty box was downloaded (I had already used it before on several projects), it only took a few minutes to bring up our local dev box:
$ git clone git@github.com:DurableDrupal/ansible-vm-platformsh.git platformsh-vk-sandbox $ cd platformsh-vk-sandbox $ vagrant up
The box is brought up in a private network with ip 192.168.19.46 (editable in Vagrantfile). So to be able to bring up the website in a local browser by name, I edited my dev box’s /etc/hosts file by including the following line in accordance with the Virtual Host template parameters (editable in provisioning/vars.yml):
$ grep platformsh /etc/hosts 192.168.19.46 platformshvk.dev
ssh into the local dev box, log into platform.sh and manage ssh keys
platformsh-vk is a newly provisioned development box, so the user vagrant hasn’t yet got a set of public and private keys for the purpose of securely authenticating onto the platform.sh account using ssh.
We first login to our local newly provisioned dev box (that’s already configured by playbook operations), also using ssh:
$ vagrant ssh vagrant@vagrant-ubuntu-trusty-64:~$ vagrant@vagrant-ubuntu-trusty-64:~$ ssh-keygen -t rsa -C "me@mymail.com" Generating public/private rsa key pair. Enter file in which to save the key (/home/vagrant/.ssh/id_rsa): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /home/vagrant/.ssh/id_rsa. Your public key has been saved in /home/vagrant/.ssh/id_rsa.pub.
The Ansible Playbook has already installed the platform.sh CLI (command-line interface), called, appropriately enough, platform, so we’re all set!
vagrant@vagrant-ubuntu-trusty-64:~$ cd /var/www vagrant@vagrant-ubuntu-trusty-64:/var/www$ ls -l total 4 drwxr-xr-x 2 root root 4096 Mar 22 16:10 html
The first time you execute the CLI you will be asked to login with your platform.sh account.
vagrant@vagrant-ubuntu-trusty-64:/var/www$ platform Welcome to Platform.sh! Please log in using your Platform.sh account Your email address: me@mymail.com Your password: Thank you, you are all set. Your projects are: +---------------+---------------------+-------------------------------------------------+ | ID | Name | URL | +---------------+---------------------+-------------------------------------------------+ | myproject | Victor Kane Sandbox | https://us.platform.sh/#/projects/myproject | +---------------+---------------------+-------------------------------------------------+ Get a project by running platform get [id]. List a project's environments by running platform environments. Manage your SSH keys by running platform ssh-keys. Type platform list to see all available command
Now, you can manage your keys from your online sandbox on platform.sh. But by using the platform CLI you can do anything from your local dev box that can be done online. To upload your public key, we did:
vagrant@vagrant-ubuntu-trusty-64:/var/www/platformsh-vk-dev$ platform ssh-keys Add a new SSH key by running platform ssh-key:add [path] Delete an SSH key by running platform ssh-key:delete [id] vagrant@vagrant-ubuntu-trusty-64:/var/www/platformsh-vk-dev$ platform ssh-key:add ~/.ssh/id_rsa.pub Enter a name for the key: vkvm
In a single line:
$ platform ssh-key:add --name=vkvm0323 ~/.ssh/id_rsa.pub The SSH key id_rsa.pub has been successfully added to your Platform.sh account
Get the project and sync the platform server and local databases
The Ansible playbook has already set up the virtual host to be edited in the file /etc/apache2/sites-available/platformshvk.dev.conf and will expect the project to be cloned under /var/www. So to get the project from the platform server, we first locate ourselves at /var/www/platformsh-vk-dev and then get the project. Permissions have already been taken care of in this directory anticipating that we are operating locally as the user vagrant in the dev box.
vagrant@vagrant-ubuntu-trusty-64:/var/www/platformsh-vk-dev$ platform get myproject Cloning into 'myproject/repository'... Warning: Permanently added the RSA host key for IP address '54.210.49.244' to the list of known hosts. Downloaded myproject to myproject Building application php using the toolstack php:drupal Beginning to build [ok] /var/www/platformsh-vk-dev/myproject/repository/project.make. drupal-7.34 downloaded. [ok] drupal patched with [ok] install-redirect-on-empty-database-728702-36.patch. Generated PATCHES.txt file for drupal [ok] platform-7.x-1.3 downloaded. [ok] Saving build archive... Creating file: /var/www/platformsh-vk-dev/myproject/shared/settings.local.php Edit this file to add your database credentials and other Drupal configuration. Creating directory: /var/www/platformsh-vk-dev/myproject/shared/files This is where Drupal can store public files. Symlinking files from the 'shared' directory to sites/default Build complete for application php
In order to sync the local database (already created with the Ansible playbook, you guessed it!) from the platform server, we must first enter the credentials (user root and name taken from the domain variable on line 105 of provisioning/playbook.yml) into the settings.local.php file created in the shared sub-directory of the project build.
vagrant@vagrant-ubuntu-trusty-64:/var/www/platformsh-vk-dev/myproject/shared$ cat settings.local.php <?php // Database configuration. $databases['default']['default'] = array( 'driver' => 'mysql', 'host' => 'localhost', 'username' => 'root', 'password' => '', 'database' => 'platformshvk', 'prefix' => '', );
Now let’s grab the drush aliases and sync databases! First I did a remote drush status on the platform
$ platform drush status
Then I grabbed the aliases:
vagrant@vagrant-ubuntu-trusty-64:/var/www/platformsh-vk-dev/myproject$ platform drush-aliases Aliases for Victor Kane Sandbox (myproject): @myproject._local @myproject.master
And used them to sync the local dev box database with what is on the platform server:
vagrant@vagrant-ubuntu-trusty-64:/var/www/platformsh-vk-dev/myproject$ drush sql-sync @myproject.master @myproject._local WARNING: Using temporary files to store and transfer sql-dump. It is recommended that you specify --source-dump and --target-dump options on the command line, or set '%dump' or '%dump-dir' in the path-aliases section of your site alias records. This facilitates fast file transfer via rsync. You will destroy data in platformshvk and replace with data from ssh.us.platform.sh/main. You might want to make a backup first, using the sql-dump command. Do you really want to continue? (y/n): y vagrant@vagrant-ubuntu-trusty-64:/var/www/platformsh-vk-dev/myproject$
Configure local dev virtual host and bring up website
We can now confirm that our local instance is alive and well via a drush status:
vagrant@vagrant-ubuntu-trusty-64:/var/www/platformsh-vk-dev/myproject$ cd www vagrant@vagrant-ubuntu-trusty-64:/var/www/platformsh-vk-dev/myproject/www$ drush status Drupal version : 7.34 Site URI : http://default Database driver : mysql Database username : root Database name : platformshvk Database : Connected Drupal bootstrap : Successful Drupal user : Anonymous Default theme : bartik Administration theme : seven PHP executable : /usr/bin/php PHP configuration : /etc/php5/cli/php.ini PHP OS : Linux Drush version : 6.6-dev Drush configuration : Drush alias files : /home/vagrant/.drush/myproject.aliases.drushrc.php Drupal root : /var/www/platformsh-vk-dev/myproject/builds/2015-03-23--11-27-44--master Site path : sites/default File directory path : sites/default/files Temporary file directory path : /tmp
We now configure the virtual host on the dev box to take into account the project name. After editing:
$ sudo vi /etc/apache2/sites-available/ <VirtualHost *:80> ServerAdmin webmaster@localhost ServerName platformshvk.dev ServerAlias www.platformshvk.dev DocumentRoot /var/www/platformsh-vk-dev/myproject/www <Directory "/var/www/platformsh-vk-dev/myproject/www"> Options FollowSymLinks Indexes AllowOverride All </Directory> </VirtualHost>
After reloading the Apache server configuration we can point our browser at the local web server

Exercise CI workflow by doing an upgrade and pushing via repo to platform
First we find out what needs updating
$ drush pm-update Update information last refreshed: Sun, 03/22/2015 - 19:21 Name Installed Version Proposed version Message Drupal 7.34 7.35 SECURITY UPDATE available
Don’t update with drush! Instead edit drush makefile and rebuild locally to test
We edit drush make file with the change in Drupal core version
$ cd repository $ vi project.make api = 2 core = 7.x ; Drupal core. projects[drupal][type] = core projects[drupal][version] = 7.35
We build locally to test
$ platform project:build Building application php using the toolstack php:drupal Beginning to build [ok] /var/www/platformsh-vk-dev/myproject/repository/project.make. drupal-7.35 downloaded. [ok] drupal patched with [ok] install-redirect-on-empty-database-728702-36.patch. Generated PATCHES.txt file for drupal [ok] platform-7.x-1.3 downloaded. [ok] Saving build archive... Symlinking files from the 'shared' directory to sites/default Build complete for application php
We confirm that the version has indeed been updated interactively in the browser.
Push to platform (automatically rebuilds everything on master!)
Wow! Talk about “everything in code”:
vagrant@vagrant-ubuntu-trusty-64:/var/www/platformsh-vk-dev/myproject/repository$ git config --global user.email me@mymail.com vagrant@vagrant-ubuntu-trusty-64:/var/www/platformsh-vk-dev/myproject/repository$ git config --global user.name jimsmith vagrant@vagrant-ubuntu-trusty-64:/var/www/platformsh-vk-dev/myproject/repository$ git commit -am "Updated Drupal core to 7-35" [master 6a0f997] Updated Drupal core to 7-35 1 file changed, 1 insertion(+), 1 deletion(-) vagrant@vagrant-ubuntu-trusty-64:/var/www/platformsh-vk-dev/myproject/repository$ git push origin master Counting objects: 5, done. Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 295 bytes | 0 bytes/s, done. Total 3 (delta 2), reused 0 (delta 0) Validating submodules. Validating configuration files. Processing activity: **Victor Kane** pushed to **Master** Found 1 new commit. Building application 'php' with toolstack 'php:drupal' (tree: b53af8a) Installing build dependencies... Installing php build dependencies: drush/drush Making project using Drush make... Executing `drush -y make --cache-duration-releasexml=300 --concurrency=8 project.make /app/out/public`... Beginning to build project.make. [ok] drupal-7.35 downloaded. [ok] drupal patched with [ok] install-redirect-on-empty-database-728702-36.patch. Generated PATCHES.txt file for drupal [ok] platform-7.x-1.3 downloaded. [ok] Moving checkout directory to `/sites/default`. Detected a `/sites/default` directory, initializing Drupal-specific files. Creating a default `/sites/default/settings.php` file. Creating the environment-specific `/sites/default/settings.local.php` file. Executing pre-flight checks... Compressing application. Beaming package to its final destination. W: Route 'www.{default}' doesn't map to a domain of the project, mangling the route. W: Route '{default}' doesn't map to a domain of the project, mangling the route. Re-deploying environment myproject-master. Environment configuration: php: size M mysql: size M redis: size M solr: size M Environment routes: http://master-myproject.us.platform.sh/ is served by application `php` http://www---master-myproject.us.platform.sh/ redirects to http://master-myproject.us.platform.sh/ https://master-myproject.us.platform.sh/ is served by application `php` https://www---master-myproject.us.platform.sh/ redirects to http://master-myproject.us.platform.sh/ To myproject@git.us.platform.sh:myproject.git f2e12d8..6a0f997 master -> master
We confirm via browser pointed to platform server that the update has been effectuated.
Next time we’ll drill down into some serious development workflow by implementing the startup landing page for the website.