Skip to main content

Conditionally rebuild a site on Netlify, based on a webhook payload

Disclosure: I am currently on the Product team at Netlify.

My blog displays webmentions using Webmention.io, and brid.gy for social media-based activity. With webmentions, other people can respond to an article via their own sites or social media profiles. That interaction can be displayed on my blog, similarly to comments (or pingbacks, if you remember those). Check out Sia Karamalegos’s in-depth tutorial on setting these up.

On my site, I display only a subset of webmention types. In the past, I configured my site on Netlify such that the site rebuilds any time I receive a new webmention. But since I only care about a few types of mentions, and like-of is my most common interaction, I wanted to prevent full builds from kicking off for new likes.

Here’s what I explored to automatically rebuild a site on Netlify based on webmention type, or in other words, based on a webhook payload.

Kicking off builds via webhook

With Netlify’s build hooks, you can trigger a fresh build based on some incoming activity from another service. From Netlify > Site Settings > Build & deploy, I created a new build hook and named it “Rebuild on Webmention”.

I then went to webmention.io > Settings > Web Hook and pasted in the build hook URL from Netlify. Now, any time I receive webmention activity, webmention.io will send a POST request to that URL, triggering a fresh site build on Netlify.

Unlike other services, webmention.io unfortunately does not have controls enabling you to filter conditions for outgoing webhooks. If it did, my work here would be done at this step! Skip to final solution

Take one: filtering webhooks via build script

Netlify has a concept of custom ignore commands, but for Historical Reasons that could probably be rethought, this command will not cancel builds triggered by build hooks.

So instead, my first solution was to use a Bash script as my build command.

  1. In Site Settings > Build & Deploy, I set my “Build command” to sh ./build.sh.

  2. I created a build.sh file in the root directory of my project:

    #!/bin/sh
    
    if echo $INCOMING_HOOK_BODY | grep "like-of"
      then exit 1
    else
      echo "Regular build"
      npm run netlify-build
    fi;
    

    This searches the $INCOMING_HOOK_BODY (an environment variable provided by the Netlify platform) for the type of webmention that I wanted to filter out, like-of. If that is found, the build will exit. Otherwise, it will run as normal using the build command configured in my package-json file (netlify-build).

  3. I tested my logic from my command line with:

    curl -X POST -d '[sample webhook payload here]' https://api.netlify.com/build_hooks/my-hook-id
    

    This sends an incoming request which triggers the build hook.

A couple notes here: the exit code needed to be exit 1, not exit 0. This unfortunately meant that these builds are a little ugly, because they are listed as failures, not cancelations.

Wouldn’t it be nice if they were categorized as cancelations…

Take two: filtering webhooks via teensy build plugin

To avoid false failures, my coworker Matt Kane suggested using a smol local build plugin instead.

This time around, I:

  1. Declared my plugin in a netlify.toml file in the root of my project:

    [[plugins]]
    package = "/plugins/filter-webhook-builds"
    
  2. Created a manifest.yml file in the /plugins/filter-webhook-builds directory with the following contents, pretty much just naming the plugin:

    name: filter-webhook-builds
    
  3. In the same directory, added index.js with the heart of the logic:

    // Cancel builds if the incoming webmention hook is of type "like-of"
    module.exports = {
      onPreBuild: ({ utils }) => {
        const hookBody = process.env.INCOMING_HOOK_BODY;
    
        if (hookBody) {
          const hookBodyJSON = JSON.parse(hookBody);
          const webmentionType = hookBodyJSON['post']['wm-property'];
    
          if (webmentionType && webmentionType === 'like-of') {
            utils.build.cancelBuild('Build canceled: "like-of" Webmention');
          }
        } else {return;}
      }
    }
    

    Probably fancier ways to write this, e.g. with a try/catch statement, but it works for me!

What this does is:

  1. Gets the webhook body, again via the INCOMING_HOOK_BODY environment variable provided by the Netlify platform.
  2. Calls the cancelBuild utility if there is a hook body, and that hook body contains the like-of identifier. Otherwise, the build runs as usual.

Builds are now cancelled if I receive a like-of webmention via webhook!

Netlify showing a build cancelled by plugin

I have also reverted my build command to npm run netlify-build, instead of the bash script I was using in “take one” of this blog post.

Wrapping up

I hope this walkthrough helps you implement similar hook-based cancelation logic to your project!

As I mentioned, how the ignore command interacts with build hooks could be rethought. The challenge for a development platform is always: how might we extend functionality without breaking existing sites relying on current behaviors? If you have a use case for a custom ignore command and build hook payloads, I would love to hear it. Feel free to drop a note on the Netlify forums—or send me a webmention!

Responses

My blog uses Webmentions. Responses from sites which likewise support Webmentions, such as Twitter or people’s personal sites, will show up here.

No Webmentions have been sent to this post yet.