Neem contact op

Gatsby websites automatisch herbouwen met behulp van een webhook

Een belangrijke schakel tijdens het automatiseren van een CI/CD flow

August 19, 2020
Cover image

We hebben een open source webhook ontwikkeld die je Gatsby webapplicatie opnieuw opbouwt zodra je content in Sanity published. Als je direct aan de gang wil met de webhook, scroll dan naar het einde van het artikel.

Bij Humanoids zijn we sinds kort bezig met het revampen van onze website. Hierbij maken we gebruik van het Gatsby framework, wat we hebben gekoppeld aan een Content Management System (CMS) genaamd Sanity. Het CMS stelt ons in staat om via het Sanity dashboard eenvoudig content op onze website en blog aan te passen. Gatsby stelt ons in staat om razendsnelle performance te garanderen, doordat Gatsby eenmalig statische pagina's gegenereerd die vliegensvlug aan de eindgebruiker kunnen worden getoond.

So far so good. What’s the catch?

De opbouw van statische pagina’s door Gatsby gebeurt niet automatisch wanneer content wordt aangepast via ons Sanity CMS. Daarnaast is het natuurlijk niet wenselijk om bij iedere content update handmatig Gatsby aan te moeten slingeren om onze website opnieuw te laten bouwen met de aangepaste content. Wat er ontbrak was dus een geautomatiseerde connectie tussen ons CMS en onze Gatsby website, die er voor zou zorgen dat onze website automatisch zou updaten wanneer er een CMS update plaatsvond. En wat doen we bij Humanoids als we er achter komen dat er nog geen bestaande oplossing bestaat voor onze digitale proble- pardon, uitdagingen? Precies ja: dan bouwen we zelf wel een oplossing!

De oplossing: een custom webhook

De essentie van onze uitdaging was dus om (1) onze Gatsby website een seintje te geven zo gauw er een update in ons CMS plaatsvond, en (2) de aangepaste content door Gatsby op te laten halen in ons CMS en deze te verwerken tot een vers aangepaste Humanoids.nl website.

Voor het eerste gedeelte van deze uitdaging vonden we al snel een aanknopingspunt bij het Sanity CMS. Sanity biedt namelijk de optie om een POST-request te maken naar een webhook. De gebruiker kan hierbij een HTTP-eindpunt opgeven bij Sanity, zodat Sanity vervolgens naar dit adres een POST-request verstuurd zo gauw er iets verandert aan de content in het CMS. Deze optie bood hoop, alleen bracht het ons bij de volgende uitdaging: hoe zorgen we er voor dat we op een specifiek HTTP-eindpunt continu luisteren naar een Sanity signaal, om dit vervolgens door te zenden naar onze Gatsby website?

Hoe de webhook werkt?

We gebruiken voor de hosting van de webhook een Express Server in Node.js die met behulp van Dockerfiles en Kubernetes wordt geupload naar een HTTP-endpoint via onze hosting, DigitalOcean. Dat is een flinke mond vol vakjargon waar we reeds over hebben geschreven in dit artikel. Geen paniek als je deze woorden niet direct weet te plaatsen, het is voor het gebruik van de webhook niet belangrijk om te weten.

Zodra het Sanity POST-request binnenkomt in onze webhook checken we eerst of de juiste ‘path parameters’ mee zijn gestuurd vanuit Sanity in de POST-request. Dit heeft als functie dat slechtwillende individuen niet zomaar rebuilds van onze Gatsby website kunnen triggeren door deze webhook te activeren. Wanneer de juiste path parameters zijn verstuurd naar onze webhook is de authenticatie voltooid en stuurt de webhook een POST-request, inclusief geautoriseerd GitHub token in de header, naar de GitHub repository die onze Gatsby website source code bevat.

const response = await fetch(
  `https://api.github.com/repos/${HUM_GITHUB_ORGANIZATION}/${HUM_GITHUB_REPOSITORY}/dispatches`,
  {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${HUM_GITHUB_TOKEN}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      event_type: `${dataset}_build`
    })
  }
);

Bouwen met GitHub Actions

Zodra de POST-request vanuit de webhook is binnengekomen op onze GitHub repository vuurt GitHub Actions een actie af genaamd 'Repository Dispatch' die vervolgens een nieuwe Gatsby build triggered met de geupdate content uit Sanity. Naast deze Gatsby build gebruiken we deze trigger ook direct om de Gatsby build online te zetten door middel van Docker, Kubernetes en onze hosting DigitalOcean.

Ok duidelijk, hoe gebruik ik dit zelf?

Om de webhook te gebruiken met je eigen Sanity en Gatsby setup is het nodig om zelf een webhook te hosten en die beschikbaar te maken op een HTTP-endpoint.

Via de webhook image op Docker Hubs kun je direct van start gaan. Hiervoor heb je wel een Docker account nodig.

Belangrijk om mee te nemen is dat de webhook draait op port 3000 en dat er verschillende environment variabelen meegegeven moeten worden:

HUM_GITHUB_ORGANIZATION = # GitHub profielnaam of organisatie
HUM_GITHUB_REPOSITORY = # Repository
HUM_GITHUB_TOKEN = # Een GitHub token dat geautoriseerd is voor het triggeren van repository dispatch events in je repository naar wens
HUM_SHARED_SECRET = # De path parameter die je toevoegt aan het eindpunt van je HTTP webhook

Tot slot is het belangrijk dat je in je Sanity dashboard de HUM_SHARED_SECRET meegeeft.

Webhooks aanmaken in het Sanity dashboard

De webhook hosten

Er zijn allerlei manieren om de webhook te hosten, zelf gebruiken we GitHub Actions en een Kubernetes configuratie op DigitalOcean.

Mocht je dit zelf ook via Kubernetes willen doen, dan zal onderstaande Kubernetes configuratie van pas komen. We starten met de deployment.yaml. Hier definiëren we de environment variabelen en de verwijzing naar de image:

# ./kubernetes/deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: sanity-webhook-github
  namespace: production
  labels:
    app: sanity-webhook-github
spec:
  replicas: 2
  selector:
    matchLabels:
      app: sanity-webhook-github
  template:
    metadata:
      labels:
        app: sanity-webhook-github
    spec:
      containers:
        - name: sanity-webhook-github
          image: humanoids/sanity-webhook-github
          env:
            - name : HUM_GITHUB_ORGANIZATION
              valueFrom:
                secretKeyRef:
                  name: sanity-webhook-github-config
                  key: HUM_GITHUB_ORGANIZATION
            - name : HUM_GITHUB_REPOSITORY
              valueFrom:
                secretKeyRef:
                  name: sanity-webhook-github-config
                  key: HUM_GITHUB_REPOSITORY
            - name: HUM_SHARED_SECRET
              valueFrom:
                secretKeyRef:
                  name: sanity-webhook-github-config
                  key: HUM_SHARED_SECRET
            - name: HUM_GITHUB_TOKEN
              valueFrom:
                secretKeyRef:
                  name: sanity-webhook-github-config
                  key: HUM_GITHUB_TOKEN
          ports:
            - containerPort: 3000
          imagePullPolicy: Always
      imagePullSecrets:
        - name: registry

Vervolgens roepen we de deployment aan vanaf de service:

# ./kubernetes/service.yaml

apiVersion: v1
kind: Service
metadata:
  labels:
    app: sanity-webhook-github
  name: sanity-webhook-github
  namespace: production
spec:
  ports:
    - port: 80
      protocol: TCP
      targetPort: 3000
  selector:
    app: sanity-webhook-github

Voordat we deze configuraties gaan toepassen is het belangrijk om de environment variabelen als secrets mee te geven:

kubectl create secret generic sanity-webhook-github-config
--from-literal=HUM_GITHUB_ORGANIZATION=<HUM_GITHUB_ORGANIZATION>
--from-literal=HUM_GITHUB_REPOSITORY=<HUM_GITHUB_REPOSITORY>
--from-literal=HUM_SHARED_SECRET=<HUM_SHARED_SECRET>
--from-literal=HUM_GITHUB_TOKEN=<HUM_GITHUB_TOKEN>
--dry-run -n production -o yaml

En dat was dat: een verandering van de data in je Sanity CMS geeft een seintje aan je custom webhook. Als je autorisatie check groen licht geeft, triggered de webhook een GitHub Action in je GitHub repository naar keuze en kan je losgaan!

We hebben een artikel geschreven over het opzetten van een Kubernetes cluster op DigitalOcean voor als je hier meer over wilt weten.


Ruben Werdmuller

Developer @Humanoids

Dylan Jongbloed

Developer @Humanoids