Deploy to Now from your CMS via a GitHub Action

Learn how to automatically rebuild and redeploy your website to ZEIT Now using a GitHub Action when some content is changed in your CMS.

Mickaël Allonneau
5 min readDec 11, 2019
ZEIT Now + GitHub Action + Contentful

I recently made a website for a client using Next.js that I wanted to host on ZEIT Now. I used Contentful for the content, and wanted the end result to be a static website for better performances, since the content shouldn’t change very often. Lastly, the build step involved a custom build.sh script because of the project’s unconventional structure :

The project’s structure
  • _build/ : The directory containing the final code to deploy, created and populated by the build.sh script

For this specific use case involving a custom build step and a custom build output directory, AFAIK it’s not possible to use Now for GitHub since it requires the output to be placed in a public/ directory, which is the directory that will be served. Because of this requirement, Now’s Deploy Hooks were a no go too.

Netlify has a Publish directory option that seem to solve this problem… but I really wanted to use Now.

So how to automatically rebuild and redeploy this app to production – in the cloud – whenever edits are made in Contentful?

The solution I ended up using involves :

  • A GitHub Action triggered on repository_dispatch, to automatically rebuild and redeploy in the cloud by simply calling a URL
  • A Contentful Webhook, to run the GitHub Action whenever some content is changed by my client in the CMS
    Note: This article demonstrates how to setup a Webhook with Contentful since it’s the CMS I used for this project, but the steps should be similar in other CMSs.

Setup the GitHub Action

The GitHub Action will simply use the now CLI to deploy the code. It’ll need a ZEIT token to pass to the CLI so it doesn’t ask to log in during the deployment.

1) Go to your repo on GitHub, click on the Actions tab, and select the Simple workflow starter workflow. Then replace the code with this :

2) Go to your ZEIT account and create a new token. Copy the generated value since we’ll need it in a second.

Back in your GitHub repo, go to Settings and then Secrets. Add a new secret named ZEIT_TOKEN (which is the identifier that is used in the Action’s source code) with the value you’ve copied before.

That’s it! Our GitHub Action is ready to use.

Setup the Contenful Webhook

Now go to your Contentful space, click on Settings (in the top bar) and then Webhooks :

Click on the Add Webhook button at the top right :

Name

Give it a Name. I went with Build & Deploy to Production so it matches my GitHub Action’s name, and since it’s explicit enough about what it does.

URL

The URL should match the following format : https://api.github.com/repos/:owner/:repo/dispatches . Just replace :owner with your username and :repo with your repository’s name :

Make sure the method is set to POST .

Triggers

You probably want your GitHub Action to be triggered only when content is published or unpublished, but not when Content Types are modified or when a draft is saved. Thus, under Triggers select Select specific triggering events and check only the relevant events :

Headers

GitHub requires a specific value for the Accept header because the repository_dispatch event we’re using in our GitHub Action is currently available for preview (which means it can change at anytime “without advance notice” so it’s not recommended to use it in production, unless you know what you’re doing).

The User-Agent header is always required by GitHub (you can put your username).

The Authorization header is required for Contentful to be allowed to trigger our GitHub Action. It should be in the form token <github-token>. You can create a GitHub token in your account’s settings (make sure to select the whole repo scope). Then add it as a secret header so it isn’t visible to anyone that has access to Contentful (remember that this token can read and write your repos !) :

Note: If your repo is public, I think the repo > public_repo scope should be enough but I haven’t tested (if anyone has, please leave a comment).

Payload

Finally, under Payload select Customize the webhook payload and add the following JSON as required by GitHub :

Here’s the code for copy / paste purposes :

{
"event_type": "contentful.content.publish_unpublish"
}

As long as it’s not empty, it doesn’t matter what the event_type field contains if you don’t need to use this value anywhere else. Just know that it will be passed to your GitHub Action, in case you need it there.

Test that everything works!

Save your Webhook! Then, go to the Content tab and update any entry. You’ll see that a new Workflow instance has been created in the Actions tab of your GitHub repo 🎉 :

You can click on it to follow the progress. Once it reaches the Deploy step, you’ll see a new deployment appear in your ZEIT project! This deployment will then be automatically promoted to production since we passed the --prod argument to now in our GitHub Action.

And voilà! 🥳

--

--

Mickaël Allonneau

Android & Web Developer with 8+ years of XP. Curious about everything but atm mostly about Static Site Generation, the JAMStack and hybrid mobile development.