Skip to content

Commit

Permalink
DOP-4352: Use custom functions for Netlify deploy events (#1024)
Browse files Browse the repository at this point in the history
  • Loading branch information
rayangler authored Feb 28, 2024
1 parent df0b1f3 commit a51f4b1
Show file tree
Hide file tree
Showing 8 changed files with 70 additions and 5 deletions.
6 changes: 6 additions & 0 deletions netlify/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Content in this folder is intended to be automatically sourced and used by builds on Netlify.

The functions folder exists to host custom functions hosted by Netlify. Currently, the ones present are automatically
triggered when one of Netlify's events match the name of the function file.
See: https://docs.netlify.com/functions/trigger-on-events/ for more information. The purpose of these triggered events
is to have a more accurate metric of when a build is actually complete, compared to Gatsby plugins' native setup.
7 changes: 7 additions & 0 deletions netlify/functions/deploy-failed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { callPostBuildWebhook } from '../../plugins/gatsby-source-snooty-preview/utils/post-build';
import { constructResPayload } from '../utils';

export async function handler(event, _context) {
const resPayload = constructResPayload(event);
await callPostBuildWebhook(resPayload, 'failed');
}
7 changes: 7 additions & 0 deletions netlify/functions/deploy-succeeded.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { callPostBuildWebhook } from '../../plugins/gatsby-source-snooty-preview/utils/post-build';
import { constructResPayload } from '../utils';

export async function handler(event, _context) {
const resPayload = constructResPayload(event);
await callPostBuildWebhook(resPayload, 'completed');
}
3 changes: 3 additions & 0 deletions netlify/netlify.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[functions."deploy-*"]
# build-hook.txt is a temporary file created at build time for Netlify builds
included_files = ['build-hook.txt', 'plugins/gatsby-source-snooty-preview/utils/post-build.js', 'netlify/utils.js']
30 changes: 30 additions & 0 deletions netlify/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import fs from 'fs';
import path from 'path';

/**
* Parses build hook data from the expected temporary location
* @returns {object | undefined}
*/
const parseBuildHookData = () => {
// This file does not currently exist, but should be created on Netlify builds as part of `npm run build:netlify`.
// The INCOMING_HOOK_BODY env var is not automatically passed along to functions, so we use a txt file to save it
const relativeFilePath = '../build-hook.txt';
const buildHookDataString = fs.readFileSync(path.resolve(__dirname, relativeFilePath), 'utf-8');
if (!buildHookDataString) {
console.log('No build hook data found.');
return;
}
return JSON.parse(buildHookDataString);
};

export const constructResPayload = (event) => {
const buildHookData = parseBuildHookData();
const parsedEventBody = JSON.parse(event.body);
// This is Netlify's default post-deployment payload. We include it with our custom data in case
// we want to process any information
const netlifyPayload = parsedEventBody.payload;
return {
netlifyPayload,
...buildHookData,
};
};
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"build": "gatsby build --prefix-paths",
"build:clean": "npm run clean && npm run build",
"build:clean:stage": "npm run build:clean && make stage",
"build:netlify": "npm run build && echo -n $INCOMING_HOOK_BODY > build-hook.txt",
"clean": "gatsby clean",
"develop": "gatsby develop",
"develop:preview": "GATSBY_IS_PREVIEW=true gatsby develop",
Expand Down
19 changes: 15 additions & 4 deletions plugins/gatsby-source-snooty-preview/gatsby-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ const {
// Global variable to allow webhookBody from sourceNodes step to be passed down
// to other Gatsby build steps that might not pass webhookBody natively.
let currentWebhookBody = {};
// Flag if the build is parsing data from Netlify or Gatsby Cloud, as there may be a difference
let isNetlifyBuild = false;

exports.createSchemaCustomization = async ({ actions }) => {
const { createTypes } = actions;
Expand Down Expand Up @@ -83,6 +85,7 @@ const getNetlifyHookBody = () => {
return;
}

isNetlifyBuild = true;
try {
const parsedPayload = JSON.parse(incomingHookBody);
return parsedPayload;
Expand Down Expand Up @@ -180,7 +183,9 @@ exports.sourceNodes = async ({
await pipeline(httpStream, decode);
console.timeEnd(`source updates`);
} catch (error) {
callPostBuildWebhook(currentWebhookBody, 'failed');
if (!isNetlifyBuild) {
await callPostBuildWebhook(currentWebhookBody, 'failed');
}
reporter.panic('There was an issue sourcing nodes', error);
}

Expand Down Expand Up @@ -240,7 +245,9 @@ exports.createPages = async ({ actions, createNodeId, getNode, graphql, reporter
`);

if (result.errors) {
await callPostBuildWebhook(currentWebhookBody, 'failed');
if (!isNetlifyBuild) {
await callPostBuildWebhook(currentWebhookBody, 'failed');
}
reporter.panic('There was an error in the graphql query', result.errors);
}

Expand Down Expand Up @@ -277,7 +284,9 @@ exports.createPages = async ({ actions, createNodeId, getNode, graphql, reporter
});
});
} catch (err) {
await callPostBuildWebhook(currentWebhookBody, 'failed');
if (!isNetlifyBuild) {
await callPostBuildWebhook(currentWebhookBody, 'failed');
}
reporter.panic('Could not build pages off of graphl query', err);
}
};
Expand All @@ -289,5 +298,7 @@ exports.createPages = async ({ actions, createNodeId, getNode, graphql, reporter
// support passing through custom data from the preview webhook's body (to include the
// Autobuilder job ID associated with the GC build).
exports.onPostBuild = async () => {
await callPostBuildWebhook(currentWebhookBody, 'completed');
if (!isNetlifyBuild) {
await callPostBuildWebhook(currentWebhookBody, 'completed');
}
};
2 changes: 1 addition & 1 deletion plugins/gatsby-source-snooty-preview/utils/post-build.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const constructSnootyHeader = (payloadString) =>
* build is finished.
* @param {object} webhookBody - The webhook body passed to the source plugin to
* initiate the preview build.
* @param {string} status - The status of the build, typically "completed" or "failed".
* @param {'completed' | 'failed'} status - The status of the build, typically "completed" or "failed".
* This value should coincide with the Autobuilder's job statuses.
*/
const callPostBuildWebhook = async (webhookBody, status) => {
Expand Down

0 comments on commit a51f4b1

Please sign in to comment.