BlogDeploy

Scheduling Netlify and Vercel builds with Github Actions

Written by Codemzy on October 28th, 2021

How can you schedule static site builds? Run a cron job! But if you don't have a server, how do you run a cron job? Do you need an extra tool or service? If you already store your code on GitHub, you have everything you need. Say hello to GitHub Actions.

If you've ever published content on a blog, there have probably been times when you want to schedule posts. You write the post one day, but you might not want it to go public until next week, next month, or even six months from now. I schedule every post I write.

And that doesn't just apply to blogs. It could be any content you need to prepare ahead of time.

If you're running your site off a server, you can tell the server when to make the content available and then get on with your life. But what about a static site?

Services like Netlify and Vercel have continuous deployment from a Git repository. They will create a fresh build of your static site when you push to your Git provider.

You can also trigger a build manually.

But what if you write a blog post for next Wednesday and configure* your blog so that it only creates static pages for posts on or before the build time.

*You can check out my static blog builder for how I do this.

You push your code, your site gets built, and the blog for next Wednesday doesn't get published yet.

Next Wednesday comes, and unless you push some more code or trigger a manual build, your blog post doesn't get published then either.

But I don't want to have to remember to manually trigger a build each time I need to publish a post that I wrote in advance. I wrote it in advance to forget about it and move on to other things.

What should I do? Create a blog calendar, and alert myself any day I have a blog post scheduled, so I can go and trigger a manual deployment? That's silly!*

*This is what I did for a blog I was running for over a year!

No, there's a much better, fully automated way.

1. Create a build hook

The first thing you need to do is create a build hook. This hook is a URL, that when called, triggers a build of your site.

If your static site is on Netlify you can create your build hook in Site settings > Build & deploy > Build hooks.

If you host your static site on Vercel, they call it a deploy hook which you can find in Settings > Git > Deploy Hooks.

If you use a different static hosting provider, check their documentation to see if they offer something similar.

2. Create the GitHub Action

Now you need to add a GitHub Action to your repository. You'll add this in the .github/workflows directory.

I've made this GitHub Action publically available: It's called Schedule Build & Deploy Hooks. You can download it for free from the GitHub Marketplace to automatically add it to your repository.

Your GitHub Action will be a YAML .yml file. Let's call it schedule-build.yml. You can use the GitHub Action I created by adding this to schedule-build.yml.

- name: Schedule Build & Deploy Hooks
  uses: codemzy/schedule-build-action@1.0.2

But maybe you want to write it yourself or tweak the schedule. So let's look at the code.

# This action triggers the build hook
    name: Schedule Build Hook Trigger

    # Controls when the workflow will run
    on:
     schedule:
     # Runs at 9am every weekday
     - cron: "0 9 * * 1-5"
     # Allows you to run this workflow manually from the Actions tab
     workflow_dispatch:
     
    # The workflow to run
    jobs:
     build:
     runs-on: ubuntu-latest
     steps:
     # Runs a request to the build hook
     - name: Build hook request
     run: curl -X POST -d {} ${{ secrets.BUILD_HOOK }}

The schedule

This action runs on a schedule - cron: "0 9 * * 1-5", which means, run at 9 am (0 9) Monday to Friday (1-5). I have no plans to schedule content at the Weekend, so I don't want to use up build minutes on those days with unnecessary builds.

If you need the action to run every day, you could change the schedule to - cron: "0 9 * * *". Maybe you want to run it earlier, at 6:30 am? - cron: "30 6 * * 1-5". Or do you want to trigger your build hourly during work hours? 0 8-17 * * 1-5.

There's a free cron schedule editor here to tweak your exact schedule.

The hook

Remember that build hook you created earlier? You can tell the action to call it. That's in the last line of code for the action:

     run: curl -X POST -d {} ${{ secrets.BUILD_HOOK }}

You don't want to put your build hook into the action directly. It will be committed to your repository. Someone could get the URL and start triggering builds and rack up build minutes.

That's why it's a secret.

When you create a Deploy Hook, a unique identifier is generated in the URL. This allows anyone with the URL to deploy your project, so treat it with the same security as you would any other token or password. If you believe your Deploy Hook URL has been compromised, you can revoke it and create a new one.

- Vercel Deploy Hooks > Security

How do we treat secrets in our code? Pop them in an environment variable. And luckily, we can do this directly on GitHub, to be used by the action. They call them Repository secrets.

So, head over to GitHub and select your repository. Add your build hook in Github Secrets under Settings > Secrets > New repository secret. The Name should be BUILD_HOOK, and the Value will be the build hook URL from Netlify or Vercel.

Run the action

Now you are all set up! Your action will run on the schedule, without any further action from you! But so you can check it all works, the GitHub Action includes the ability to run the workflow manually.

On GitHub, select your repository and go to Actions. Select the action from the Workflows menu, and click Run action.

If you don't see your action on GitHub yet, make sure you committed the action in Git and pushed it to your remote repository.

P.S. If you are using Cloudflare Pages to host your static site, you don't need a GitHub action, you can schedule the build within Cloudflare. Here's a blog post I wrote with instructions on scheduling builds with Cloudflare.