Arturas Smorgun

software engineer, a nice guy


Pre-push hook to save yourself from breaking build

In order to minimize risk of accidentally breaking build
As a developer
I want code style checks and automated tests to be run each time I push code into repository.

As far as I know pre-push hook is available in git since version 1.8.2. It lives in .git/hooks/pre-push and will run every time you push code:

$ git push
✔ PHPCS
✔ PHPSpec
✔ Behat
Counting objects: 5, done.
Delta compression using up to 3 threads.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (5/5), 562 bytes | 0 bytes/s, done.
Total 5 (delta 4), reused 0 (delta 0)
To git@git.github.com:company/project.git
   642d074..aa8daff  develop -> develop

If script exits with error state then git command is aborted. Here is example script which would run php code style fixer, phpspec and behat on vagrant virtual machine:

#!/bin/sh

runViaVagrant() {
    vagrant ssh -c "cd /mnt/project && $2 > /dev/null"
    if [ $? -ne 0 ]
    then
        echo " ✘ $1 has failed - push aborted"
        exit 1
    else
        echo " ✔ $1"
    fi
}

# go to project root
cd ./$(git rev-parse --show-cdup)/

# run scripts
runViaVagrant 'PHPCS' './bin/php-cs-fixer fix --quiet --dry-run src'
runViaVagrant 'PHPSpec' './bin/phpspec run --quiet'
runViaVagrant 'Behat' './bin/behat'

# success
exit 0

In this example I used sample vagrant box for php projects (available from my github repository).

Published on 25 of February, 2014

Incenteev ParameterHandler, Composer, Capifony

In Symfony projects parameters.yml is a place to store environment specific parameters. To keep project repository clean parameters.yml.dist with sample values is usually checked in instead of real configuration.

To deal with these files there is Incenteev ParameterHandler tool.

It allows to interactively add missing parameters from .yml.dist to .yml. It also removes parameters from .yml if they are missing in .yml.dist as well.

This will be done each time you install or update project dependencies with Composer. It will also run on each deployment if you are using Capifony for that.

To keep outdated parameters you can reconfigure default behavior via composer, but better and cleaner way is to always keep all the relevant sample parameters in your .yml.dist file.

P. S. Just in case if you wondering how to solve it via configuration, here is code snippet for you:

{
    "extra": {
        "incenteev-parameters": {
            "keep-outdated": true
        }
    }
}
Published on 24 of February, 2014

Configure SSH agent forwarding in Vagrant

When managing project dependencies with tools like composer then at some point you will inevitably have a dependency on private library sitting somewhere in private git repository. On production this is solved easily by adding deployment key. In case of developer's virtual machine solution is not hard either: SSH agent forwarding. It can be configured in Vagrantfile in one line (next to configurations of provider and provisioner):

config.ssh.forward_agent = true
Published on 15 of February, 2014

Tagging features in Gherkin and Behat

Syntax is pretty easy. All you have to do is to put any free form text (prepended with @) on top of feature or scenario:

@sprint-1 @appname-123 @subsystem-name
Feature: Customer can add products to wishlist
  In order to remember products I like
  As a store customer
  I want to store products I like in wishlist

  @wip
  Scenario: Add to wishlist
    Given I am on homepage
    And there is "Awesome item" in product list
    When I add "Awesome item" into wishlist
    Then I get notification message confirming addition to wishlist
    And "Awesome item" is persisted into wishlist associated with current session

Having sprint number, task handle and subsystem tags on feature and work in progress on scenario allows combinations of different tags to control which scenarios to run. I'm using behat, so here is how to filter running features and scenarios:

# run all the features from sprint 1
bin/behat --tags="@sprint-1"

# run all the features related to task appname-123
bin/behat --tags="@appname-123"

# run only scenario which I'm working on
bin/behat --tags="@appname-123&&@wip"
Published on 13 of February, 2014

Split Varnish cache by user agent

I know I know, responsive design is all over the place nowadays and discussing dedicated mobile theme is kind of forbidden and lame. Anyway this approach is still widely used and I want to show how to split Varnish cache by user agent. It is useful when back end you are using this value to decide which view to use and you want to leverage from caching as well.

In Magento, for instance, you can specify different theme according to user agent. When you put varnish in front of given installation, you have to use similar approach. It's trivial and only a slight modification of default vcl configuration - to start considering user agent in cache key hash so that pages would be cached separately.

Adding literal user agent value to cache key would just explode it though - there are so many variations of it's value. To solve it we can create method, which would normalize it. For instance if we care only if it's "mobile" or "desktop" browser, if could look something like this:

sub normalize_useragent {
    if (req.http.user-agent ~ "Mobile|iPhone|Android") {
        set req.http.X-UserAgent = "mobile";
    } else {
        set req.http.X-UserAgent = "desktop";
    }
}

And then we can use this normalized value in cache key hash generation:

sub vcl_hash {
    hash_data(req.url);
    call normalize_useragent;
    hash_data(req.http.X-UserAgent);
    return (hash);
}
Published on 14 of November, 2013

Debug PHP Cli with Xdebug and PhpStorm

PhpStorm is well known for it's zero-configuration web application debugging using Xdebug. And it works just fine with remote hosts as well. However, debugging of php command line code on remote host is not that straight forward.

But it's not that hard as well. Basically, we have to set up couple of environment environment variables. One is Xdebug configuration of remote host with IDE, another - IDE configuration with server name, where script is running:

# specify remote host IP address (where IDE is running) -> 192.168.33.1
export XDEBUG_CONFIG="idekey=session_name remote_host=192.168.33.1 profiler_enable=1"

# specify server name -> project.development.local
export PHP_IDE_CONFIG="serverName=project.development.local"

To terminate debugging session it's only required to unset these two variables:

unset XDEBUG_CONFIG
unset PHP_IDE_CONFIG
Published on 06 of November, 2013

Git post checkout hook for purging Magento caches

Probably anyone who ever worked with Magento knows that it heavily relies on caches. Which, obviously, is good for speeding things up, but as a developer sometimes I find it quite annoying to have to clear it all the time when switching between features in development. I store my projects in Git and each feature is isolated into separate branch, so most obvious way to automate it was to use post checkout hook.

Git hooks are very straightforward. They are small custom scripts, which fire off when certain events happen. By default there are few sample executable bash scripts, which live in .git/hooks directory.

I use Vagrant for firing up and managing project virtual machine and usually have Redis and Varnish setup for caching. First I flush these service caches in my vagrant box, then I remove file caches. Like this:

#! /bin/sh

# go to vagrant path
cd ./$(git rev-parse --show-cdup)/tools/vagrant

# restart cache services in box
vagrant ssh -c 'redis-cli flushall && sudo /etc/init.d/redis restart && sudo /etc/init.d/varnish restart'

# go back to project root
cd ../../

# flush file caches
rm -Rf public/var/cache/*
rm -Rf public/var/full_page_cache/*
rm -Rf public/media/js/*
rm -Rf public/media/css/*
rm -Rf public/media/css_secure/*

# celebrate!
echo 'flushed magento caches'

Right. Looks slow. But is it too slow? On my laptop it takes up to 8 seconds. I still have to realize how painfully show it is in reality, but so far was okay - just enough time to sip my tea or water from a glass.

Published on 05 of November, 2013