Cloudflare Pages Build Status Notifications

Hey folks, just looking for some guidance. Weā€™ve got build triggers set up with CloudFlare Pages, and while these do work I donā€™t currently get any build notifications. I can configure webhooks for both successful and failed build events, but I canā€™t specify the payload to include the status flag. Example of a correct payload below.

curl -n -X POST https://webhooks.datocms.com/XXXXXXXXXD/deploy-results \
  -H 'Content-Type: application/json' \
  -d '{ "status": "success" }'

Below is an example of what Cloudflare includes in its webhooks for deployment events.

{
  "name": "Webhook Test",
  "text": "\nāš”ļø Cloudflare Pages: A production deployment has started for Project Name āš”ļø\n\nBuild Link: https://dash.cloudflare.com/xxx/pages/view/Project Name/11111111111\nCommit Hash: b2c77bd4590bdcb0cb4849ced0b9cd08a64963f482d0c0f5d5b3757b730bee15\nEnvironment: ENVIRONMENT_PRODUCTION\nTimestamp: 2022-10-27T06:00:22Z\n\n\n",
  "data": {
    "alert_name": "pages_event_alert",
    "account_tag": "xxx",
    "project_id": "11111111111",
    "deployment_id": "11111111111",
    "project_name": "Project Name",
    "event": "EVENT_DEPLOYMENT_STARTED",
    "environment": "ENVIRONMENT_PRODUCTION",
    "commit_hash": "xxx",
    "custom_domain": "example.com",
    "pages_dev_url": "example.com",
    "branch_alias_url": "example.com",
    "preview_url": "example.com",
    "timestamp": "2022-10-27T06:00:22Z"
  },
  "ts": 1681832973,
  "account_id": "xxx",
  "policy_id": "xxx",
  "alert_type": "pages_event_alert"
}

Is there any known way of configuring notifications from Cloudflare to DatoCMS for these build events?

Hello @macdara and welcome to the community!

From what i understood, Cloudflare pages does not allow you to set a custom payload to the webhook they send, and therefore you canā€™t send back to Dato the webhook with the payload we specified, is that correct?

Hey @m.finamor - yes, thatā€™s correct. Iā€™m wondering if thereā€™s a known way of changing the webhook Dato expects to receive? Not sure if thatā€™s an option via a plugin or some other settings. Itā€™s not the end of the world right now, as there are only developers working on the site and we can see the build statuses, but it will be quite awkward once we have our marketing team working on the site.

Hello @macdara , for now not unfortunately :frowning:
The webhook expected by Dato is not modifiable for now, but you should open a feature request for it, as i can see how it could be useful specially for cloudflare pages in this case
Sorry about that

Weā€™ve worked around this issue by executing the npm run build script within a bash script that tracks the exit code of that script and triggers the right DatoCMS success / error webhook accordingly:

#!/bin/bash

run() {
    npm run build
    local exit_code=$?

    if [[ "${CF_PAGES}" == "1" && "${CF_PAGES_BRANCH}" == "main" ]]; then
        echo "Production environment."

        [ $exit_code == 0 ] \
            && datocms_status="success" \
            || datocms_status="error"

        notifyDatocms $datocms_status
    else
        echo "Non-production environment."
    fi
}

notifyDatocms() {
    local status=$1
    echo "Notify DatoCMS of deploy status: $status"
    if [ "$status" == "success" ]; then
        curl -n -X POST https://webhooks.datocms.com/XXXXXXXXXX/deploy-results \
            -H 'Content-Type: application/json' \
            -d '{ "status": "success" }'
    else
        curl -n -X POST https://webhooks.datocms.com/XXXXXXXXXX/deploy-results \
            -H 'Content-Type: application/json' \
            -d '{ "status": "error" }'
    fi
}

run
3 Likes

Inspired by @devoorhoede Iā€™ve quickly created a node script myself:

import { exec } from 'child_process';

exec('npm run cloudflare:pages', async (err) => {
  console.log(
    err ?
      `Something went wrong when running cloudflare:pages, exited with error code: ${err.code}` :
      'cloudflare:pages build successfully!'
  );

  const res = await fetch('https://webhooks.datocms.com/XOXOXOXOX/deploy-results', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ status: err ? "error" : "success" }),
  });

  console.log(
    res.ok ?
    'DatoCMS received the status correctly' :
    `Something went wrong when sending the status to DatoCMS, response code: ${res.status}`
  );
});

My package.json has 2 extra scripts:

"cloudflare:build": "node deploy/build-trigger.mjs",
"cloudflare:pages": "npx @cloudflare/next-on-pages@1",

The cloudflare:build is the command Iā€™m supplying to Cloudflare as build command.
The cloudflare:pages is the actual command to run the cloudflare pages build command.

Happy coding yall! <3

2 Likes

Can you explain to me how you did this? We are having the same issue with the build hooks.

As an aside, might it be simpler to just proxy the existing Cloudflare success/failure notification hooks through a serverless function (like a Cloudflare Worker) to have it rewrite it into Dato format?

They provide an example on their blog for similar use cases:
https://blog.cloudflare.com/cloudflare-relay-worker

Edit: Added exit code propagation to the below script to ensure a failed build stops the deploy!

Inspired by @thomas.verleye above weā€™re using the script below. The main difference is that the raw log output from the build script is piped through (even shows colors in Cloudflareā€™s logs) so you can follow the build process live.

It also adds some config at the top to map branch-names to Dato build triggers. I guess most setups have one or two long lived branches such as main/master and staging that benefit from build triggers while other short lived feature branches donā€™t need them as they will be deployed automatically on every code change.

const { spawn } = require('child_process')

// This script notifies Dato on pages build status

// For each added build trigger on: https://syb-storefront.admin.datocms.com/project_settings/build_triggers/{projectid}/edit
// Extract the random string from the Status notifications webhook URL and add it here:
const branchToStatusNotificationSecret = {
  main: '{the random string from the build notification}',
  staging: '{the random string from the build notification}',
}

// Build the app
const npmRun = spawn('npm', ['run', 'cloudflare:pages'])
npmRun.stdout.setEncoding('utf8')
npmRun.stderr.setEncoding('utf8')

npmRun.stdout.on('data', (data) => {
  process.stdout.write(data)
})

npmRun.stderr.on('data', (data) => {
  process.stderr.write(data)
})

npmRun.on('exit', async (code) => {
  console.log(
    code
      ? `Something went wrong when running cloudflare:pages, exited with error code: ${code}`
      : 'cloudflare:pages build successfully!',
  )

  const branch = process.env['CF_PAGES_BRANCH']
  const statusNotificationSecret = branchToStatusNotificationSecret[branch]

  if (!statusNotificationSecret) {
    console.log(
      `Will not notify of build status as no status notification secret is configured for branch: ${branch}`,
    )
    process.exit(code)
  }

  const res = await fetch(
    `https://webhooks.datocms.com/${statusNotificationSecret}/deploy-results`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ status: code ? 'error' : 'success' }),
    },
  )

  if (!res.ok) {
    console.log(
      `Something went wrong when sending the status to DatoCMS, got: ${res.status} ${res.statusText}`,
    )
    res.text().then(console.log)
  } else {
    console.log('DatoCMS received the status correctly')
  }

  process.exit(code)
})

@dthreatt To use this setup you login to Cloudflareā€™s dashboard, navigate to your Workers & Pages project and then to the tab Settings and the sub section Builds & deployments. There you change the Build configurations. Set the Build command to npm run cloudflare:build instead of the default npx @cloudflare/next-on-pages@1.

You also need to add the scripts mentioned by @thomas.verleye to your package.json file.

1 Like