Automating ConvertKit Broadcasts with GitHub Actions

Creating the GitHub Actions Workflow

IIRC, all you've gotta do to get started is define a .github/workflows/main.yml like the below at the root of your repository and set the workflow to be triggered by any push to the main branch (or whatever branch you'd like). Push it up to GitHub and head to the Actions tab on your GitHub repo home page and you should see some stuff starting to happen. Good deal.

name: Blog Update Notification
on:
  push:
    branches:
      - main
# ...

Setting Up Your GitHub Repository

Next, define the job notify-subscribers and make the first step checking out the repository so the workflow actually has access to the files. Use the sparse-checkout feature to focus on only the specific directories we'll need (.github for our workflow files/scripts and src/app/articles for our posts). This optimizes the checkout process by ignoring irrelevant files, and might save a few microseconds or something.

Right after that step you're gonna need to install Node for what comes next, so slap that in there too:

# ...
jobs:
  notify-subscribers:
    runs-on: ubuntu-latest
    steps:
      - id: checkout_repository
        name: Checkout repository
        uses: actions/[email protected]
        with:
          sparse-checkout: |
            .github
            src/app/articles
          sparse-checkout-cone-mode: false

      - id: set_up_nodejs
        name: Set up Node.js
        uses: actions/setup-node@v2
        with:
          node-version: '14'
      # ...

Detecting Article Changes

Nice. Now getting a little meatier, the detect_article_changes step executes a bash script that checks for modifications in your blog articles, ensuring only relevant changes trigger the subsequent steps.

# ...
- id: detect_article_changes
  name: Detect article changes
  run: bash ./.github/workflows/detect_article_changes.sh
# ...

I'm not going to go nuts breaking down the script, but I'll provide it for you here. You will likely need to customize it a bit for your setup, but it should be a good starting point. Don't forget to chmod +x before pushing it up. The key lines are:

# ...
# Get the file changes in src/app/articles directory between the last two commits
changed_files=$(git diff --name-status HEAD^ HEAD -- 'src/app/articles' | grep -E '\.md$|\.mdx$' || true)
# ...

and:

# ...
# Get the date of the last commit
last_commit_date=$(git log -1 --format=%cd --date=short)
# ...

...because we'll only want to continue if there are changed_files and the article_date is later than or equal to the last_commit_date (ignores updates to old articles; it shouldn't send out a notification for a fix to an ancient article).

Integrating ConvertKit and Sending Notifications

Last but not least we have this bad boy. This step is only executed if we've actually had an article meet our criteria (it's new) and we've set env.ARTICLE_DATA previously with the post's metadata. After that, environment variables are set for integrating with ConvertKit's API: CK_API_KEY is securely stored in GitHub secrets, while the base URL and endpoint variables are defined in either regular old environment variables or repository vars.

# ...
- id: send_ck_email
  name: Send email via ConvertKit
  if: ${{ env.ARTICLE_DATA }}
  env:
    ARTICLE_DATA: ${{ steps.detect_article_changes.outputs.article_data }}
    CK_API_KEY: ${{ secrets.CK_API_KEY }}
    CK_API_BASE_URL: ${{ vars.CK_API_BASE_URL }}
    CK_API_BROADCASTS_ENDPOINT: ${{ vars.CK_API_BROADCASTS_ENDPOINT }}
  run: bash ./.github/workflows/send_ck_email.sh

Here's that script. It's pretty straightforward and uses curl to hit the endpoint with data pulled from the ARTICLE_DATA environment variable. Gonna need to chmod +x that one too.

Conclusion

And that's it for today. Using VS Code? Do yourself a favor and install the github.vscode-github-actions extension to make viewing job status and setting variables a bit quicker right from within your editor.

Stay up to date

Get notified when I publish something new, and unsubscribe at any time.