Building Middleman using GitHub Actions

My site now posts from anywhere thanks to GitHub actions!

I used this blog as a jumping off point, but there were a few things I had to modify:

  • there’s an update to the action/checkout action, so version bump.
  • to handle the node and ruby dependencies, I’m using the provided actions.
  • my changelog relies on having access to the full git history. By default, actions/checkout only pulls the HEAD commit. I added fetch-depth to fix that.

The final file in .github/workflows/main.yml:

name: CI
      - master
    runs-on: ubuntu-latest
      - uses: actions/[email protected]
          fetch-depth: 0
      - name: Configure AWS Credentials
        uses: aws-actions/[email protected]
          aws-access-key-id: {{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: {{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: us-east-1
      - name: Setup node
        uses: actions/[email protected]
          node-version: '13.x'
      - name: Install node dependencies
        run: npm ci
      - name: Setup ruby
        uses: ruby/[email protected]
          bundler-cache: true
      - name: Install ruby dependencies
        run: bundle install
      - name: Build static site
        run: bundle exec middleman build
      - name: Deploy static site to S3 bucket
        run: aws s3 sync ./build/ s3://{{ secrets.BUCKET_NAME }} --delete

After some pushing and (shameful) git commit --amend, it’s working!

As always, I write in Drafts. In the course of writing this post, I wound up hacking some quick actions in Drafts to make this process easier.


The first takes a typical Draft and write it to Working Copy in the format that Middleman expects.

// compute the date
let date = draft.createdAt;
// compute the filename
let title =
let filename_title =
    .replace(/[^a-zA-Z0-9\-]/g, "-")
let filename = `${strftime(date, "%F")}-${filename_title}.html.markdown`
let template =

title: ${draft.displayTitle}
date: ${strftime(date, "%F %R %Z")}


let cb = CallbackURL.create();
    cb.baseURL = "working-copy://x-callback-url/write/";
    cb.addParameter("key", "itsasecretdonttell");
    cb.addParameter("repo", "");
    cb.addParameter("path", `source/articles/${filename}`);
    cb.addParameter("text", template);
    cb.addParameter("x-success", "working-copy://");

// open in Working Copy
let success =;

// error handling
if (success) {
else {;

The next action is a convenience action for me… since my previous action bases the file name on the CreatedAt date of the Draft (which is stable) I can wind up in a position where I work on a post for a few days before I post it. Running “Update Date to Last Edit” does this:

draft.createdAt = draft.modifiedAt;

Very ugly. Works just fine.

The last action uses Shortcuts to select a photo, place it in Working Copy in the correct month folder, and write the path back to the Draft.


Honestly… this post will be the first attempt… so let’s see if it even works!

  • 2021-01-05 19:02:39 +0000

    Add details about `fetch-depth`

    Thanks to @tmiller for his insight.

  • 2021-01-01 14:51:30 +0000

    fix the yaml

    I think the drafts action does something weird to `{{` when it's sent
    over urlschemes… I'll look into it.

  • 2021-01-01 14:45:14 +0000

    update tags

  • 2021-01-01 14:41:57 +0000

    Update Date

  • 2021-01-01 14:41:24 +0000

    update date to today