GitHub Label

Finally got around to fixing Sparky

Back in 2010, the iPad was all the rage. The first really usable tablet device, sporting lightning-fast multitouch capabilities, that were even exposed as (then non-standard) HTML5 Web APIs for JavaScript developers to play with.

It wasn't always easy to keep up with the latest trends, and trying out multitouch experiments then required an iPad. So I wrote a touch event simulation tool, called addTouch. The principle was to use the mouse to add, manipulate and remove multiple touch points, dispatching touch events to the browser.

This made it possible to develop multitouch experiences and test them using an average desktop browser. I supported both Apple's touch events and the (now extinct) Mozilla touch events.

Sparky : just a demo app

Sparky in action To demonstrate what addTouch could do, I put together a simple demonstration app called Sparky. Its principle is very simple — put your fingers on the screen to create sparking lines that you can move around. This was in the really early days of HTML5 support, and I hadn't learned Canvas drawing yet, so my implementation uses a few <div> elements and some CSS transform magic.

This has worked fine for almost six years now. I just had to fix a small issue when Apple released the iPhone 4, which had a high-dpi ("Retina") display. There were a few versions of the iOS Safari browser that had bugs when it came to handling device pixels vs logical pixels. I had to do a lot of experimenting with this, and wrote a StackOverflow answer that is currently my third most up-voted one.

What started out as just a demo app for addTouch, now has a life of its own. After my latest complete blog remake, I have been keeping a close look at the IIS logs that I get from Azure. It turns out that Sparky still gets a few hundred visits per week from different incoming sources, which is nice, but it prompted me to do some much-needed spring cleaning.

Update

The code was actually not too shabby, but some of it was really outdated. It still supported the old Mozilla touch events, and it used screen coordinates instead of local coordinates, which made it look pretty horrible in a non-fullscreen view, especially on the Android Chrome browser. That needed to change.

It was written in a certain style, specifically catered to my own old JavaScript minifier which I no longer use. Also, it polluted the global window object, which I never do anymore, unless I'm writing public APIs. That needed to change, too.

Other than that, it was just the small issue of estimating screen DPI, where I had used hard-coded values directly adapted for various iOS devices. That needed to be replaced with some more general-purpose code, and after a little head-scratching, I just decided to go with the same hard-coded value for all devices. At least for now.

The result is now live and kicking on spark.attrakt.se, and the complete code is available in the lbrtw/sparky GitHub repository.

Next step

I will try to fix some of the issues with addTouch as well, but I'll save that exercise for later. It is not very useful anymore, because the major browsers and development environments have really good touch emulation built in.

Try it out: spark.attrakt.se

Posted by Anders Tornblad on Category JavaScript Labels
Tweet this

First version of GitHub Webhook handler public on GitHub

Yesterday, I wrote about my efforts for creating an easy-to-use GitHub Webhooks handler in PHP, suitable for shared hosting environments.

After a few hours of making the code a little prettier, it is now public on GitHub. I remade the whole thing into an API style that I would enjoy using. Now, you can hook yourself up to GitHub Webhooks like this:

<?php
require_once "mt-github-webhook.php";

// Changes in the QA branch are pushed to the secret password-protected QA web site
\MT\GitHub\Webhook::onPushToBranch("qa-testing")->
                    forChangesInFolder("main-web-site/public_html")->
                    setGitHubCredentials("github-username", "My5ecretP@ssw0rd")->
                    pushChangesToFolder("/www/sites/qa.domain.com/public_html");

// Changes in the PRODUCTION branch are pushed to the public-facing web site
\MT\GitHub\Webhook::onPushToBranch("production")->
                    forChangesInFolder("main-web-site/public_html")->
                    setGitHubCredentials("github-username", "My5ecretP@ssw0rd")->
                    pushChangesToFolder("/www/sites/www.domain.com/public_html");
?>

The clone url is: https://github.com/lbrtw/mt-github-webhook.git. Feel free to fork and play around with it.

Posted by Anders Tornblad on Category PHP Labels
Tweet this

Automatic deployment on shared server using GitHub webhooks

If you, like me, have a few spare time projects, chances are you don't own or rent a dedicated server for your web hosting. I use Loopia (a Swedish web hosting provider) for my hosting purposes. I use their web hotel service, so I have very little control over file system paths, php modules and such.

On a dedicated server, using GitHub webhooks is pretty straightforward. When your server gets notified of a push or a closed merge request, you can do a simple git clone to get a fresh full copy of the branch you are using for your deploys. On a shared system, without access to the git command-line tools, it gets a little tricker.

I have developed a php-based solution that works for me. My branch and merge setup looks something like this:

feature-xOne branch per featuremasterMain development branchdevAuto-publish to a dev environmentvnextAuto-publish for demonstration purposeswwwCurrent running version, stableStarted work on new navigationBug fixNew artwork addedBug fixesBug fixes mergedBug fixNew navigation doneTest of new navigationCustomer demoBug fixLive deployment

Some development is performed in the master branch, and larger features are developed in branches of their own. Whenever a feature makes enough progress to be visible or usable (or is completed), or a bug is fixed, I merge to the dev branch. Every now and then, I'm not the only coder making changes. When other coders are done with a feature or a bug-fix, they create a pull request that I approve to perform the merge.

The dev branch is where we test everything internally. We can do experiments, move stuff around, temporarily remove features or add wild and crazy stuff. When the dev branch is good enough for showing to people, we merge to the vnext branch, which is always a little more stable and feels more "done". This is where customers can check out future features and have their say in stuff.

After a couple of rounds of pushing to vnext, it's time to go live. This is done by merging to the www branch.

Continuous Integration and Deployment

Every time something gets pushed into the non-master branches, GitHub posts a message to my webhook handler. The handler reads the message payload to find out what files are changes and what branch is the target. Using this information, it downloads the correct source files from raw.githubusercontent.com and copies to the correct directory of the shared web server file system.

// We are only interested in PUSH events for now
$eventName = @$_SERVER['HTTP_X_GITHUB_EVENT'];
if ($eventName != 'push') {
    http_response_code(412);
    exit("This is not a PUSH event. Aborting...");
}

// Read and parse the payload
$jsonencodedInput = file_get_contents("php:\/\/input");
$inputData = json_decode($jsonencodedInput);

// What branch is this?
$branchRef = $inputData->ref;

// If I'm interested in the branch, copy all changes, otherwise quit
if ($branchRef == 'refs/heads/dev') {
    copyChanges('/WEB-HOTEL-ROOT/dev.domainname.com/', 'dev', $inputData);
} else if ($branchRef == 'refs/heads/vnext') {
    copyChanges('/WEB-HOTEL-ROOT/vnext.domainname.com/', 'vnext', $inputData);
} else if ($branchRef == 'refs/heads/www') {
    copyChanges('/WEB-HOTEL-ROOT/domainname.com/', 'www', $inputData);
} else {
    http_response_code(412);
    exit("I'm not interested in the $branchRef branch. Aborting...");
}

The code above is simple enough. Depending on the type of event, and on the name of the branch, the script either exits immediately with a nice error message (that you can read in your GitHub repository's webhook settings page), or calls the copyChanges function, shown below.

// Copy changes
function copyChanges($rootFolder, $branchName, $inputData) {
    // Check all commits involved in this push for changes that I'm interested in
    $interestingChanges = extractInterestingChangesFromCommits($inputData->commits);
    $changedPaths = array_keys($interestingChanges);

    // No interesting changes? Quit!
    if (count($changedPaths) == 0) {
        exit("No interesting changes. Goodbye!");
    }

    foreach ($changedPaths as $localPath) {
        $fullPath = $rootFolder . $localPath;
        $changeType = $interestingChanges[$localPath];

        if ($changeType == 'delete') {
            // Deleted file - delete it!
            unlink($fullPath);
        } else {
            // Added or modified file - download it!
            $url = "https://USERNAME:PASSWORD@raw.githubusercontent.com/USERNAME/REPOSITORY/$branchName/$localPath";
            $fileContents = file_get_contents($url);
            if ($fileContents !== false) {
                file_put_contents($fullPath, $fileContents);
            }
        }
    }
}

Actually, the code I use contains some more error checking. It also recursively creates new directories if a file wants to be put in a directory that does not yet exist.

// Extract interesting changes
function extractInterestingChangesFromCommits($commits) {
    // This function returns an array where
    //     the keys are local file paths, and
    //     the values are the type of change
    // Something like this:
    // [
    //     'path/file.1' => 'add',
    //     'path/file.2' => 'change',
    //     'path/file.3' => 'delete'
    // ]

    $result = [];

    foreach ($commits as $commit) {
        foreach ($commit->added as $added) {
            $result[$added] = 'add';
        }
        foreach ($commit->modified as $modified) {
            $result[$modified] = 'change';
        }
        foreach ($commit->deleted as $deleted) {
            $result[$deleted] = 'delete';
        }
    }

    return $result;
}

That's about it for now. The script has been running and handling deployments for my spare-time projects for a while now, and I feel confident about it. I'll make some more touchups to this script, and then I'll put it on GitHub for you to star. Check in for a link in a few days.

Posted by Anders Tornblad on Category PHP Labels
Tweet this