3 min read

Publishing JAM Stack Web Apps with GitOps and GitHub Actions

Justin Yoo

In my previous post, we discussed how to set schedules for your JAM stack based static websites with GitHub Actions and Azure Durable Functions. But it didn't complete the final piece – scheduling automation. Throughout this post, I'm going to implement that scheduling part to be automated.

JAM Stack in Short

JAM stands for JavaScript, API and Mark-up. What does this mean to me? Let me give you an example. There are many static website generators such as Jekyll, Hugo, Gatsby, VuePress, Gridsome, etc. When we write a post using Markdown syntax, the generator converts those markdown documents into HTML ones as a front-end application. If it needs to communicate with its back-end API, JavaScript makes the communication possible. Of course, JavaScript is used to enrich the front-end application. This website is built with Gridsome as well.

GitOps Automation

We've implemented the workflow below, except for the red arrow part, in the previous post.

The red arrow is responsible for, after creating a PR, sending an API request to Azure Durable Functions endpoint. I wasn't able to find out a simple way of automating this part. However, I realised that PR is another type of events that GitHub Actions can capture.

Therefore, I put the timestamp for scheduling in the PR request body so that the GitHub Actions workflow can recognise it.

Here's the GitHub Actions workflow only triggered by PR. Not every PR-related event triggers this workflow, but only the opener does it (line #8). And for debugging purpose, I put the PR event payload to be rendered (line #20).

name: Pull Request
on:
pull_request:
branches:
- dev
types:
- opened
jobs:
schedule_publish:
name: Schedule publish
runs-on: ubuntu-latest
steps:
- name: Check event payload
shell: bash
run: |
echo ${{ toJSON(github.event) }}
view raw 01-pr-flow-1.yaml hosted with ❤ by GitHub

After running this workflow once, we can see this payload structure showing the PR request body.

Let's trim the timestamp from the body field. I use the regular expression to get the timestamp and store it to the published output value (line #5).

- name: Check pr body
id: prbody
shell: bash
run: |
echo ::set-output name=published::$(echo ${{ toJSON(github.event.pull_request.body) }} | sed 's!published: !!g' | sed 's!\\r\\n!!g')
view raw 02-pr-flow-2.yaml hosted with ❤ by GitHub

The published output value can be actually verified with the following action. As the action defined above uses the ID of prbody, we can access to the output value by steps.prbody.outputs.published.

- name: Check environment variables
shell: bash
run: |
echo "Published: ${{ steps.prbody.outputs.published }}"
view raw 03-pr-flow-3.yaml hosted with ❤ by GitHub

Now we got the timestamp for scheduling. We need to send it to the Azure Durable Functions API endpoint, within the GitHub Actions workflow. Here's the curl command that sends the request.

- name: Schedule publish
shell: bash
run: |
curl -X POST 'https://${{ secrets.AZURE_FUNCTIONS_NAME }}.azurewebsites.net/api/orchestrators/schedule-event' \
-d '{ "owner": "aliencube", "repository": "blog", "issueId": ${{ github.event.pull_request.number }}, "schedule": "${{ steps.prbody.outputs.published }}" }} }' \
-H "x-functions-key: ${{ secrets.AZURE_FUNCTIONS_KEY }}" \
-H "Content-Type: application/json"
view raw 04-pr-flow-4.yaml hosted with ❤ by GitHub

Therefore, instead of sending the API request like below:

The PR workflow takes the responsibility, and the whole workflow has been automated.

All we need to do is to create a PR with a timestamp for publishing schedule. The workflow will take care of the rest, and we'll be able to see the post published.


So far, we've fully automated post scheduling with GitOps, Azure Durable Functions and GitHub Actions. If you are running a static website and have the code in GitHub, this automation will reduce your workload significantly.