Netlify Build Plugin for SpeedCurve

#

Netlify hosted their JAMStack Conf in San Francisco this past Wednesday. Quibbles with the JAMStack name aside, there were some great talks in the schedule and they’ve started to fill up my watch later list.

One thing that Netlify announced during the event was their new build plugins functionality. Netlify’s build process now exposes different events during the build and deploy lifecycle that you can use as hooks to attach certain functionality to. The simplest way to do that at any sort of scale is to create a build plugin that you can then install for any site you may want to use it.

It’s not all that different from any other sort of build process, I suppose, but it does give Netlify some continuous integration functionality which is nice.

I really like Netlify, and I really like SpeedCurve so I thought building a SpeedCurve plugin for Netlify would be a fun way to play around with the new feature.

Getting setup

The first step was signing up for the private beta and getting access to build plugins in the first place.

There wasn’t much to do at all to get started playing with the build lifecylcle and plugins locally. I had to make sure I had a netlify.yml configuration file for my site and use the latest version of the Netlify CLI, and that was about it.

To enable build plugins remotely, in the live Netlify Account, requires a teeny, tiny bit of setup. I’m afraid I can’t tell you that or the Netlify team will probably lock me out of my account and send Phil Hawksworth over to “tie up the loose ends” or something.

What I will say is that, in addition to the setup they suggested, I had to also change my build image from Ubuntu Trusty 14.04 (their legacy build image) to Ubuntu Xenial 16.04 (their current default). Don’t let the Linux names and versions scare you—these were the only two options and all I had to do was tick a radio button.

Building the plugin

Building the plugin itself turned out to be fairly painless. The build documentation was pretty helpful, though I mostly relied on Sarah’s post and the demo plugins she links to.

As I mentioned, there are a number of different lifecycle events that a plugin can hook into. In this case, my goal was to trigger a round of tests in SpeedCurve for each deploy, so it made sense to hook into the finally event. So, I created a folder for the plugin, made an index.js file, and setup the basic structure:

module.exports = {
    async finally() {
        console.log('Preparing to trigger SpeedCurve tests');
    }
}

That little bit of code is all that’s really necessary to do something during the finally event of the lifecycle. To test it locally, you then add a plugins section to your netlify.yml configuration file:

build:
  # this is basic configuration stuff telling
  # netlify where to publish to and what command
  # to run to build the site
  publish: dist
  lifecycle:
    build:
      - eleventy
plugins:
  # here's where we pull in the plugin
  speedcurveDeploy:
    type: ./plugins/netlify-plugin-speedcurve-deploy

What you call the plugin in the YAML file doesn’t really matter, as long as the path to the plugin is correct.

So with that setup, I was able to use the Netlify CLI to confirm things were going to work alright by running:

netlify build --dry

That command spits out a bunch of information, but what’s relevant here is that it tells you what steps are going to run and what actions are attached to them.

Screenshot of the output from running netlify build --dry, showing the lifecycle hooks and the SpeedCurve deploy plugin as attached to the finally event

The SpeedCurve API docs are pretty straightforward about how to trigger a deploy. Alongside some helpful options, there are two bits of information that are required: the API key and the Site ID.

The API key, at least, is sensitive information, so it made sense to put that in an environment variable (Netlify makes it easy to add them to your configuration in their admin area). I decided to also place the Site ID in an environment variable. I could’ve used the configuration feature in the YAML file, but it just seemed to be neater to put it all together in one spot for now.

Once again, I followed Sarah’s lead (never a bad idea) on neatly pulling in those variables into my plugin’s index.js file`:

const {
    env: {
        //Your SpeedCurve API Key (Admin > Teams)
        SPEEDCURVE_API_KEY,
        SPEEDCURVE_SITE_ID
    }
} = require('process')

All that was left was to use that data to fire off the test. I pulled in node-fetch (because I find the ergonomics of the Fetch API very nice) and then used that to trigger the SpeedCurve tests from within the finally event already setup:

fetch('https://api.speedcurve.com/v1/deploys', {
    method: 'POST',
    headers: {
        'Authorization': 'Basic ' + Buffer.from(SPEEDCURVE_API_KEY + ':' + 'x').toString('base64'),
        "Content-type": "application/json",
        "Accept": "application/json",
        "Accept-Charset": "utf-8"
    },
    body: JSON.stringify({
        site_id: SPEEDCURVE_SITE_ID
    })
})
.then(function (data) {
    if (data.status == 200) {
        console.log('SpeedCurve test submitted!');
    } else {
        console.log('SpeedCurve test couldn\'t be submitted. Status: ' + data.statusText);
    }
})
.catch(function (error) {
    console.log('Error: ', error);
});

And that was it. I ran a local build and things worked smoothly and, after tweaking the build image, a remote deploy worked on the first try as well. Now, each time I deploy a new version of the site, SpeedCurve will automatically run a series of tests so I can quickly see if I’ve changed anything from a performance perspective.

Better yet, because it’s built as a plugin, setting this up for any other Netlify sites I have will take only a minute or two.

Imperfections

There are a few things I’d like to tweak. The finally lifecycle event doesn’t technically fire after the deploy right now (something Netlify is working on fixing). That’s not a huge issue here because the Netlify build process is so fast that by the time SpeedCurve actually runs the tests, the new site has been deployed. Still, it should really be run after the deploy occurs just to be safe.

Because it runs a little early, there’s also currently no way to get the deploy ID or anything else to make it a bit easier to track the change in SpeedCurve. I’d love to be able to pass back the deploy ID to the SpeedCurve API once it’s available. For now, SpeedCurve sees no title applied so it just uses the current date to identify the deploy.

None other than Sir Hawk Philsworth himself pointed me in the direction of some docs that I completely whiffed on. Turns out, you can grab the git commit hash using an environment variable, so I’ve updated the plugin to pass that along to SpeedCurve.

The SpeedCurve deploy screen, showing how SpeedCurve references the lastet deploy by using the date.

I think it’s still handy in its current form, so if you have access to the beta and want to give it a whirl, I’ve put the code up in a repo and added the plugin to npm as well if that’s your cup of tea (it does make using the plugin a hair easier, I think).

All in all though, Netlify build plugins seem pretty slick and straightforward. I’m a big proponent of baking basic optimizations and checks and balances into your build process, and Netlify’s build lifecycle gives us another place to hook into.

I’ve got a few other plugins in mind already that I think will be pretty helpful. Now to find the time to build them.