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.
-
In Site Settings > Build & Deploy, I set my “Build command” to
sh ./build.sh
. -
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 mypackage-json
file (netlify-build
). -
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:
-
Declared my plugin in a
netlify.toml
file in the root of my project:[[plugins]] package = "/plugins/filter-webhook-builds"
-
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
-
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;} } }
What this does is:
- Gets the webhook body, again via the
INCOMING_HOOK_BODY
environment variable provided by the Netlify platform. - Calls the
cancelBuild
utility if there is a hook body, and that hook body contains thelike-of
identifier. Otherwise, the build runs as usual.
Builds are now cancelled if I receive a like-of
webmention via webhook!
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!