Continuous deployment for free when building a mobile iOS app and API

Background

I am a big fan of CI and CD and the overall mentality that you should always be deploying. There should never be any stress involved in the fact that you are deploying. You should always be able to trust your code.

While building my latest hobby project, one of the first thing I started doing was to setup all the environments that was needed to make sure I don’t break anything as the project grows. Since it is a hobby project, I would prefer to get as far as I can with free tiers available at different providers.

In this article I will outline my setup and how you can grow into the paid plans at each provider while taking away the initial costs. Making sure that the only initial cost is a smaller amount of development time.

Technology Stack

The backend is a Ruby on Rails API that is serving JSON. This gives me a few things I need to test in the backend:

  • API Requests
  • Controllers
  • Models

All of the above are tested using RSpec.

The mobile iOS app uses Cedar as a test framework and I pesonally strive to maintain a good test coverage (of course, where it makes sense).

CircleCI (iOS)

CircleCI just opened up mobile iOS support as a beta feature and you can request access to it by sending them an email and then activate it:

CircleCI now offers Beta support for building and testing iOS (and OSX) projects. To enable this feature, go to Project Settings > Experimental Settings and enable the “Build iOS project” experimental setting. This will cause builds for this project to be run on OSX machines rather than the usual Linux containers, and iOS-related build and test commands will be automatically inferred.

They offer one private project for free which suits us perfectly, since we only need one for the iOS app.

Fabric (iOS)

Beta by Crashlytics is a cross-platform toolset built to make beta distribution easy. I followed a sample project that was available for CircleCI and Testflight and modified that to get my CircleCI to push my builds to Fabric.

Codeship (Web)

Codeship is a continuous integration & delivery as a service provider. They also offer a free tier with these restrictions:

  • 1 concurrent build
  • 1 test pipelines
  • 100 private builds / month
  • 5 private projects

It will not scale very well if we start deploying a lot, but it will get you quite far to start off with. They have an awesome support for Ruby on Rails so in this case, its extremely easy to get up and running!

Heroku (Web)

Heroku is a cloud platform as a service. That means you do not have to worry about infrastructure; you just focus on your application. They offer instant Deployment with Git push, where we will instead push our code to GitHub and let Codeship run the CI and deploy to Heroku instead if everything gets a green light

Slack (All)

In my opinion, Slack is like IRC but for companies. You can create open channels for the projects, groups and topics that the whole team shares. I suggest you create a #notifications channel where all your CI posts their status updates when a build fails (or passes). This way the entire team knows when something goes awry.

The Next Thing: Joining Citrix

Last week I finished my assignment at Aftonbladet for Netlight Consulting in Stockholm. Next week I will start my new job as a Senior Software Engineer for Citrix Online.

The last few years I feel like I’ve accomplished a lot, I’ve successfully started two businesses alongside my studies; my own consulting company Framert AB and the Swedish tech blog Swedish Startup Space. I have learnt a lot from these experiences and after finishing my Master’s degree, I started working for Netlight Consulting. After all, that’s what most of my friends from university was doing and they seemed to like it. That was a great journey for me and a great company, but I wanted something more.

The opportunity at Citrix was brought to my attention by a good friend of mine, and I managed to get a great connection with the team. I will be working with iOS and there are some interesting opportunities and obstacles we will need to tackle together.  The majority of the team is based in Santa Barbara, California and me and a few others in the team will be working remotely.

I am really excited to see where this path takes me and what the future holds for me and the people close to me. Most of all, I am really excited to start working across the borders and get back into mobile app development.

Cache Bust WordPress Assets Based On Git Revision

Cache busting is to rewrite a URL for an asset (JS, CSS, images, etc) so that is unique. This way, you can cache them for as long as you want. If the URL changes, it will use the new file.

I am using deployments tool to checkout the latest version of a given branch, therefore I know that there will be a .git folder on my production server.

The idea is to use the current Git commit as a version number using git head reference.

function project_get_current_git_commit( $branch = 'master', $length = false ) {
  if ( ! defined( 'PROJECT_CURRENT_GIT_COMMIT' ) ) {
    $hash = file_get_contents( sprintf( '%srefs/heads/%s', dirname(__FILE__), $branch ) );

    define( 'PROJECT_CURRENT_GIT_COMMIT', ( $hash ? $hash : false ) );
  }

  return ( $length ? substr( PROJECT_CURRENT_GIT_COMMIT, 0, $length ) : PROJECT_CURRENT_GIT_COMMIT );
}

// Enqueue WordPress asset
wp_enqueue_script('application','/assets/javascripts/application.min.js', array('), project_get_current_git_commit( 'master', 8 ), true);

Voilá, now you can cache bust everything based upon your latest Git revision.

Of course, there are other ways to do this as well. Another solution is to store environment variables that contains the latest revision and then read it from PHP, that would save some I/O from the physical file.

Installing WordPress core via Composer and finding the proper rewrites for Nginx or Apache

The idea is to separate the WordPress site’s components into their own directories right at the root. This creates a simple modular structure, where parts of a WordPress site are totally on their own. This makes things less complex and less prone to errors.

We will not be touching the WordPress subdirectory, we will only update it when a new version is released. Therefore, we will have to put our configuration and custom development (themes & plugins) outside the WordPress folder.

I’m going to use Composer package manager for PHP to install these packages and this is the directory structure that I want to create.

Directory structure

website/
--config/ 
--vendor/
--lib/
--www/
    wp/ - Will contain the WordPress package
    wp-content/ - Will contain all my themes and plugins
    .htaccess
    index.php
    wp-config.php
composer.json

Composer file

The Composer can be used for both the core and plugins, for plugins we will use wpackagist. After you’ve installed composer, create a composer.json in your root folder that looks something like this:

{
  "name": "parse/demo",
  "description": "Parse: Demo",
  "license": "proprietary",

  "repositories": [
    { "type":"composer", "url":"http://wpackagist.org" },

    { "type": "package",
      "package" : {
        "name": "wordpress",
        "type": "webroot",
        "version": "3.7.1",
        "dist": {
          "type": "zip",
          "url": "https://github.com/WordPress/WordPress/archive/3.7.1.zip"
       },
        "require" : {
          "fancyguy/webroot-installer": "1.1.0"
        }
      }
    }
  ],

  "require": {
    "composer/installers": "1.0.x-dev",
    "wordpress": "3.7.1",
    "wpackagist/mp6": "~2.1"
  },

  "require-dev": {
    "wpackagist/debug-bar": "~0.8",
    "wpackagist/debug-bar-console": "~0.3"
  },

  "minimum-stability": "dev",

  "extra": {
    "wp-content": "www/wp-content",
    "webroot-dir": "www/wp",
    "webroot-package": "wordpress",
    "installer-paths": {
      "www/wp-content/plugins/{$name}/": ["type:wordpress-plugin"]
    }
  }
}

This file defines that we want to install WordPress core in a folder www/wp/ and its plugins in www/wp-content/plugins/

index.php

By moving the WordPress core, the index.php that is in www/ will now have to be changed to:

<?php
// WordPress view bootstrapper
define( 'WP_USE_THEMES', true );
require( './wp/wp-blog-header.php' );

wp-config.php

And since we will not use the wp-content/ folder in www/wp/wp-content/, we need to redefine that in wp-config.php:

<?php
define('WP_CONTENT_DIR', dirname(__FILE__) . '/wp-content');
define('WP_CONTENT_URL', 'http://' . $_SERVER['HTTP_HOST'] . '/wp-content');

Now, we have set up the files, your vhost need to be configured to the www/ folder.

Nginx

After struggling a bit with PHP’s PATH_INFO variable and found a solution, this is the important part:

if (!-e $request_filename) {
  rewrite /wp-admin$ $scheme://$host$uri/ permanent;
  rewrite ^(/[^/]+)?(/wp-.*) /wp$2 last;
  rewrite ^(/[^/]+)?(/.*\.php)$ /wp$2 last;
}

location / {
  root   /home/username/apps/appname/current/www/;
  try_files $uri $uri/ /index.php?$args;
  index index.php;
}

location ~ \.php$ {
  fastcgi_pass 127.0.0.1:9000;
  fastcgi_index index.php;
  fastcgi_split_path_info ^(.+\.php)(/.+)$;
  fastcgi_param SCRIPT_FILENAME /home/username/apps/appname/current/www$fastcgi_script_name;
  fastcgi_param ENVIRONMENT production;
  fastcgi_param PATH_INFO $fastcgi_script_name;
  include /etc/nginx/fastcgi_params;
}

Apache

    ServerName domainname.com
    DocumentRoot "/home/username/apps/appname/current/www/"
    <Directory "/home/username/apps/appname/current/www/">
        Options Indexes Includes FollowSymLinks  
        AllowOverride All
        Order allow,deny
        Allow from all

The .htaccess in this case will look something like this:

Options -Indexes

RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]

# add a trailing slash to /wp-admin
RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L]

RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) wp/$2 [L]
RewriteRule ^([_0-9a-zA-Z-]+/)?(.*\.php)$ wp/$2 [L]
RewriteRule . index.php [L]

To upgrade plugins or core, you simply need to update your composer.json. We still need to add custom plugins into our repository, but you could create your own repositories for them as well.