Deploying Greenkeeper PR Branches with Surge and Codeship

As I've mentioned before, Greenkeeper is pretty great. Another tool I love is Surge, which allows you to quickly and easily publish any static content to the web. These two tools can be combined in a pretty cool way to allow you to see a built version of your site anytime Greenkeeper finds a release that doesn't match your versioning strategy.

Note: I'm using Codeship as my CI service here, but any should be able to do this in a similar fashion.

Prerequistes

You'll want to have your project setup to run on Codeship first. The Getting Started docs are a great place to start, or Surge has their own documentation on how to integrate with Codeship.

Configure a new deploy pipeline

The first step is to configure Codeship to run a custom command whenever a new branch starting with greenkeeper is pushed to. Note that this could be feature instead if you wanted to deploy every time you created a feature branch as well.

Branch starts with greenkeeper

Then you want that deploy pipeline to run the command npm run deploy-branch. I like to use an npm run-script as that allows me to keep all the logic for deploying this in my project.

npm run deploy-branch

Configure your run scripts

You probably already have some sort of command to deploy your project when the master branch gets pushed. Now you need to add a deploy-branch script that will get run by the deploy pipeline you added above. The only difference between this and your normal deploy command is that the domain is specified and will use the $CI_BRANCH environment variable from Codeship. You'll need to replace <PROJECT_NAME with your actual project name too.

{
  "scripts": {
    "deploy-branch": "surge --project public/ --domain $CI_BRANCH-<PROJECT_NAME>.surge.sh"
  }
}

Teardown old projects

One issue with this approach is that this will create a new Surge project with every branch you create (that matches the deploy pipeline). This is fine, because your branch names will be unique, but eventually you'll want to cleanup these old projects.

Thankfully, I wrote a script called surge-teardown-branches that does just that!

To use it, you'll first want to install it with npm install surge-teardown-branches --save-dev. And then amend your current deploy script to use it:

{
  "scripts": {
    "deploy": "surge --project public/ && surge-teardown-branches <PROJECT_NAME>.surge.sh"
  }
}

What this will do is look for Surge projects matching <BRANCH_NAME>-<PROJECT_NAME>.surge.sh and teardown any where <BRANCH_NAME> is not a remote branch.

See the project README for more info about how it works.

Greenkeeper

The last step is to make sure Greenkeeper is enabled on your project. Now whenever a new version of a library is published, Greenkeepr will do it's magic and create a branch like greenkeeper-react-16.0.0 on your project. Codeship will deploy that to a Surge project at the domain greenkeeper-react-16.0.0-myproject.surge.sh, and you can check it out to make sure everything looks good! Once your merge and delete the Greenkeeper branch, the no longer needed Surge project will be torn down.

Feature branches (Optional)

If you want to do with feature branches, the only thing you need to change is the branch name prefix when configuring your deploy pipeline.

This enables the following workflow (let's say your project is called awesomesauce and you prefix branches with feature):

  1. Create new branch feature-new-stuff on your awesomesauce git repo
  2. Push to that branch a whole bunch. Each push will deploy the project to feature-new-stuff-awesomesauce.surge.sh
  3. Once you're done with that branch, merge feature-new-stuff to master and delete the branch
  4. The subsequent push to master will run the deploy script which will and feature-new-stuff-awesomesauce.surge.sh will be torn down

React Invariant Violation / Minified Exception on iOS 8 with Webpack + Babel

TL;DR

Check out this gist to see the bug.

I ran into a head-scratcher over the weekend that I needed document, because I spent a few more hours than I wanted to trying to see why a deployed project wasn't working at all on iOS 8.4.

My project is using react, webpack, and babel. One of the babel plugins I'm using is babel-plugin-transform-react-inline-elements which transforms react elements to increase performance in production.

I'm also using the babel-polyfill (which includes the core-js shim) to polyfill some features like Symbols.

The project was working fine locally on iOS 8, but when deployed I was getting an error from react:

Error: Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.

If I removed the babel-polyfill, the error went away. The problem was I needed the babel-polyfill especially on iOS 8 where there is no Symbol. I also tried just loading core-js/shim or just core-js/es6/symbol but the error was still happening. Here's a way stripped down version of my main JS file which was causing the error:

// The error goes way with no polyfill, but I need the polyfill
import 'babel-polyfill';

import React from 'react';
import DOM from 'react-dom';

DOM.render(
  <div>hey everybody</div>,
  document.getElementById('root')
);

Next, I removed the webpack define plugin so it was no longer replacing process.env.NODE_ENV so I could see what the unminified error was:

Invariant Violation: ReactDOM.render(): Invalid component element. This may be caused by unintentionally loading two independent copies of React.

I had a feeling that I wasn't actually loading two different copies of react, so I kept digging. It was then that I remembered I had some babel plugins turned on in production only. My .babelrc looked like this:

{
  "presets": [
    "react",
    "es2015"
  ],
  "env": {
    "production": {
      // Yay! Removing this plugin fixes it!
      "plugins": [
        "transform-react-inline-elements"
      ]
    }
  }
}

Once I disabled the transform-react-inline-elements plugin the error went away. Since this plugin is only used for performance improvements, I decided that working on iOS 8 was more important.

I put together a full gist of the bug to make it easier to reproduce. I still don't know exactly why this error is happening, but I plan on trying to report it to the relevant projects to see if it can be tracked down and fixed. The main lessons I learned though:

  • Remove process.env.NODE_ENV overwriting from your webpack bundle so you can see the error messages from react. See the DefinePlugin docs for more info
  • Babel environment specific plugins can lead to environment specific bugs. Always turn these off as a first step to debugging a production only bug.

Greenkeeper

Over the course of ~36 hours last weekend greenkeeper notified me of two of my projects where the build was broken by minor updates to tools that I was using.

Here is one of the nice PRs, from what is quickly becoming one of my favorite services:

One was a bug in a babel plugin that I use to treeshake my lodash methods to decrease the overall bundle size, and the other was a change to eslint that broke babel-eslint.

While it's kind of a bummer to get emails that your projects can't build right now due to changes you didn't make it, that is more than offset by knowing about it almost so quickly and being able to easily pin the dep to the previous working version.

In the case of babel-plugin-lodash greenkeeper notified my in less than 8 minutes (!!), and I was able to fix my project, go to the module's repo, find out that the issue wasn't reported yet, come up with test case to reproduce the bug only in the latest version, and hopefully save other developers time in tracking down the issue.

But my favorite part is now that I have a few projects with pinned dependencies, greenkeeper will then notify me of the next update to the package and see if my software is working again. If it is working, all I have to do is merge the pull request and I'm back on the latest version knowing that my software is working as it did before.

I now have greenkeeper enabled on 6 of my bigger open source projects, and it would be a no brainer to pay for to use on private projects.

Update 08 Mar 2016

Greenkeeper opened a PR 4 minutes and 27 seconds after babel-plugin-lodash was published and then a little bit later my CI tests on Travis notified me that the build from the PR was passing. I had to make one small change to set the version back to a range, since greenkeeper assumes you always want to keep the same type of version declaration (which is the desired behavior in most other cases).

Thanks again Greenkeeper!

npm@3: What Ends Up at the Top of node_modules with Conflicting Dependencies?

npm@3 has been stable for a few months now and one the big changes was the new flat(ter) directory structure inside node_modules. This blog post has a good writeup of why that's a good thing.

You'll notice that I said "flat(ter)" above. If two modules in your project have conflicting dependencies, then one of those will end up on the top level of node_modules and the other will be nested inside its parent's node_modules directory. This got me to wondering, which ones ends up on the top level?

I was wondering this because of a discussion on the eslint issue tracker about if the new directory structure will allow you to require a nested dependency now that it is at the top level of node_modules. As the linked comment pointed out, it will let you, but that doesn't mean you should do it. If you did depend on this functionality, you would be requiring a module without any guarantee of what version you would be getting back.

Read more

Secret Santa over SMS with Twilio

TL;DR

Use this code to pick Secret Santas over SMS with Twilio.

My family does a sibling Secret Santa (plus spouses) every year. We live in a few different cities, and we forgot to do it on Thanksgiving so we were out of luck as far as picking in person. My sisters know what I do for a living so they joked over text that I should "write a code". (Software is truly eating the world.)

I knew there were services out there to do remote Secret Santa, but they were all over email. I knew everyone's phone number and I knew that they would check their texts far more often than their email, so I really wanted something to deliver the recipients over SMS.

I found a Ruby script out there to do the same thing, but I wanted some additional logic and its been a few years since I wrote Ruby, plus I've always wanted to play around with Twilio. Sounds like a recipe for a nice Saturday project.

Read more