diff --git a/.gitignore b/.gitignore index 773d707e9c9d0..e78fa72d9a1a5 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,8 @@ public/ # User-specific editorconfig files .editorconfig + +# Netlify Functions build output +package-lock.json +functions/ +node_modules/ diff --git a/Makefile b/Makefile index 3bcfcd6093a70..366671f09f5b2 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,8 @@ DOCKER = docker HUGO_VERSION = 0.49 DOCKER_IMAGE = kubernetes-hugo DOCKER_RUN = $(DOCKER) run --rm --interactive --tty --volume $(PWD):/src +NODE_BIN = node_modules/.bin +NETLIFY_FUNC = $(NODE_BIN)/netlify-lambda .PHONY: all build sass build-preview help serve @@ -16,6 +18,9 @@ build: ## Build site with production settings and put deliverables in ./public build-preview: ## Build site with drafts and future posts enabled hugo -D -F +functions-build: + $(NETLIFY_FUNC) build functions-src + check-headers-file: scripts/check-headers-file.sh diff --git a/functions-src/deploy-succeeded.js b/functions-src/deploy-succeeded.js new file mode 100644 index 0000000000000..afe5c7e5936a1 --- /dev/null +++ b/functions-src/deploy-succeeded.js @@ -0,0 +1,77 @@ +"use strict"; + +const + { IncomingWebhook } = require('@slack/client'), + kubernetesSiteRoot = 'https://kubernetes.io', + fetch = require('node-fetch').default, + { SLACK_WEBHOOK_URL } = process.env; + +const webhook = new IncomingWebhook(SLACK_WEBHOOK_URL); + +// A random smattering of Kubernetes documentation pages +// We can add as many pages here as we'd like +const kubernetesEndpoints = [ + 'docs/home', + 'docs/tutorials/configuration/configure-redis-using-configmap', + +] + +// Ensure that the SLACK_WEBHOOK_URL environment variable is set +const checkEnv = () => { + if (!SLACK_WEBHOOK_URL) { + return { + statusCode: 422, + body: "[FAILURE] The Slack webhook URL must be set via the SLACK_WEBHOOK_URL environment variable" + } + } +} + +// This function posts a warning message to Slack +const sendSlackMessage = (msg) => { + const slackMessageObject = { + username: "noindex checker", + text: msg + } + + // Send the message to the webhook + webhook.send(slackMessageObject, (err, res) => { + return (err) ? { statusCode: 422, body: `[ERROR] Slack webhook error: ${err}` } : + { statusCode: 200, body: `[SUCCESS] Response received from Slack: ${JSON.stringify(res)}` }; + }); +} + +// Iterate through each Kubernetes endpoint to check for noindex headers +const checkEndpoints = () => { + kubernetesEndpoints.forEach((endpoint) => { + const url = `${kubernetesSiteRoot}/${endpoint}`; + + fetch(url) + .then(res => { + const headers = res.headers; + + if ('x-robots-tag' in headers.raw() && (headers.get('x-robots-tag') == 'noindex')) { + const msg = `[WARNING] "X-Robots-Tag: noindex" header found on the following page: ${url}`; + + // Send Slack notification + sendSlackMessage(msg); + + return { statusCode: 404, body: msg }; + } else { + const msg = `[SUCCESS] No improper X-Robots-Tag: noindex headers found on ${url}`; + + return { statusCode: 200, body: msg }; + } + }) + .catch(err => { + return { statusCode: 422, body: err }; + }); + }); +} + +// The handler function +exports.handler = async (event, context) => { + checkEnv(); + + // Below are the various deploy succeeded checks + checkEndpoints(); +} diff --git a/netlify.toml b/netlify.toml index bc48100868a40..7cc9defa8e4ae 100644 --- a/netlify.toml +++ b/netlify.toml @@ -3,6 +3,7 @@ # It is turned off for only for the production site by using [context.master] below # DO NOT REMOVE THIS (contact @chenopis or @sig-docs-maintainers) publish = "public" +functions = "functions" command = "make non-production-build" [build.environment] diff --git a/package.json b/package.json new file mode 100644 index 0000000000000..d1cf0bd89dfb5 --- /dev/null +++ b/package.json @@ -0,0 +1,10 @@ +{ + "private": true, + "devDependencies": { + "@babel/core": "^7.0.1", + "@slack/client": "^4.4.0", + "babel-loader": "^8.0.2", + "netlify-lambda": "^0.4.0", + "node-fetch": "^2.2.0" + } +}