Jan 4, 2024 | github actions, laravel forge

How to Trigger a Webhook on a Schedule Using GitHub Actions

Recently I added a feature to the speaking page on this site: a list of the most popular episodes of The Laravel Podcast for this week.

However, this site is powered by a static generator, so the data on that page will go out of date pretty soon after each deploy.

I considered setting up a local cron job on my server to run the deploy script once a day, which is certainly my easiest option. But I didn't want to duplicate my deploy script in cron that I had already configured in my host, Laravel Forge.

Thankfully, Forge gives you an easy webhook to trigger your existing deploy script, so I figured that'd be my cleanest option.

The plan

I'll want to call my webhook, which we'll approximate as https://forge.laravel.com/webhooks/my-webhook-yay, once every day.

First, I set up a new GitHub Action workflow in my project. I created a YAML file for the workflow at .github/workflows/deploy-on-schedule.yml, with the following basic contents:

name: Deploy Every Day

To learn more about how this works, check out GitHub's official documentation on using GitHub Actions.

How to call a webhook URL from a GitHub Action workflow

There are two main ways to call a URL from a GitHub Action workflow: with curl, and with a published GitHub Action. I want you to see how simple it is to call curl yourself, but it's no faster than the GitHub Action, so use whichever is more comfortable for you.

Calling a URL with curl

You can call any arbitrary shell code directly from your workflow, so you can build a curl command directly in your action:

jobs: 
  webhook: 
    name: Ping webhook
    runs-on: ubuntu-latest 
    steps: 
      - name: Use curl to ping webhook
        run: | 
          curl -n "https://forge.laravel.com/webhooks/my-webhook-yay"

If you want to add headers or extra content to the request, you can flesh out your curl command as much as you want, passing \ at the end of each line:

jobs: 
  webhook: 
    name: Ping webhook
    runs-on: ubuntu-latest 
    steps: 
      - name: Use curl to ping webhook
        run: | 
          curl -n "https://forge.laravel.com/webhooks/my-webhook-yay" \
          --header 'Content-Type: application/json' \
          --data '{"some-datadata":"here"}'

Calling a webhook with webhook-action

There's also a GitHub Action dedicated to calling external URLs, joelwmale/webhook-action.

Here's the syntax for what we're doing here:

jobs:
  webhook:
    name: Ping webhook
    runs-on: ubuntu-latest
    steps:
      - name: Use webhook action to ping webhook
        uses: joelwmale/webhook-action@2.3.2
        with:
          url: https://forge.laravel.com/webhooks/my-webhook-yay

If you want to learn more about how to use this action, including how to pass headers and data, check out this introductory post.

How to schedule a GitHub Action to run regularly

Now that we've built out the ability to call a webhook from our workflow, how do we call a workflow on our own schedule?

If you're familiar with GitHub Actions, you're likely familiar with the on property, which allows you to define what events trigger this workflow running. Normally, we'd attach it to Git events—push to a certain branch, for example.

But we can also define it on a schedule, using the same syntax we use for cron:

on:
  schedule:
    - cron: "0 0 * * *"

The above schedule will run our workflow once a day, at the end of the day.

You can take a look at the GitHub Actions docs for schedule if you'd like to learn more.

Putting the Pieces Together

So let's put these together into a single workflow file:

name: Deploy Every Day
on:
  schedule:
    - cron: "0 0 * * *"
jobs: 
  webhook: 
    name: Ping webhook
    runs-on: ubuntu-latest 
      steps: 
        - name: Use curl to ping webhook
          run: | 
            curl -n "https://forge.laravel.com/webhooks/my-webhook-yay"

Or, if you want to use the Action:

name: Deploy Every Day
on:
  schedule:
    - cron: "0 0 * * *"
jobs:
  webhook:
    name: Ping webhook
    runs-on: ubuntu-latest
    steps:
      - name: Use webhook action to ping webhook
        uses: joelwmale/webhook-action@2.3.2
        with:
          url: https://forge.laravel.com/webhooks/my-webhook-yay

Two more tricks, and a caveat

That's it for building out our main workflow! But I have two more tricks to share that helped me in building this action.

Trick 1: Running your workflow manually using workflow_dispatch

If you're building a workflow that is only triggered in certain circumstances (whether it's a given push, or on a schedule), you may want to manually trigger it some times, especially when you're first building it out. But how?

There's another event (defined under the on: property of our config) that will serve us in this circumstance: workflow_dispatch. There's a lot of work you can do to customize workflow_dispatch, but for now let's just enable it without parameters:

name: Deploy Every Day
on:
  workflow_dispatch:
  schedule:
    - cron: "0 0 * * *"
jobs: 
  webhook: 
    name: Ping webhook
    runs-on: ubuntu-latest 
      steps: 
        - name: Use curl to ping webhook
          run: | 
            curl -n "https://forge.laravel.com/webhooks/my-webhook-yay"

Now that we have that entry there in our YAML, we can manually trigger a run of this workflow. Open up your repo in GitHub; choose the "Actions" tab, and choose your relevant workflow in the left section. You'll now see a banner saying "This workflow has a workflow_dispatch event trigger.", with a "Run workflow" button next to it. You can use this button to manually trigger runs of this workflow!

GitHub Actions 'deploy now' button

Trick 2: Passing secrets into your call

Let's say you want to extract the specific URL out of your code and instead store it in GitHub secrets. Let's take a quick look at how you'd do that.

First, for the GitHub Action, which is simple:

jobs:
  webhook:
    name: Ping webhook
    runs-on: ubuntu-latest
    steps:
      - name: Use webhook action to ping webhook
        uses: joelwmale/webhook-action@2.3.2
        with:
          url: ${{ secrets.webhook_url }}

It's a bit more complex to pass an environment variable into bash, but it's still quite manageable:

jobs: 
  webhook: 
    name: Ping webhook
    runs-on: ubuntu-latest 
      steps: 
        - name: Use curl to ping webhook
          env:
            WEBHOOK_URL: ${{ secrets.webhook_url }}
          run: | 
            curl -n "$WEBHOOK_URL"

Caveat: Forge specifically

While I'm using Laravel Forge as the context here, this post is really just about how to schedule GitHub Actions workflows and how to call URLs within them. However, if you are using Forge, Tightenite Guillermo Cava Nuñez pointed out that Forge has its own GitHub Action.

Additionally, the Forge documentation points out how to use the Forge CLI to run more than just deploys in your GitHub Action workflows, if you're interested.

The end

That's it! I hope you learned something useful!


Comments? I'm @stauffermatt on Twitter


Tags: github actions  •  laravel forge

Subscribe

For quick links to fresh content, and for more thoughts that don't make it to the blog.