Will Murphy's personal home page

Scheduled Posts Workaround

One feature I really miss from WordPress is the ability to schedule posts in the future. I used to post to that blog every Wednesday morning, not by actually getting up and writing a post every Wednesday, but by writing posts whenever I felt inspired, and always scheduling them for the soonest Wednesday that didn’t have a post yet. Now that I’m trying to blog again, I’d like to do the same thing.

But scheduling posts on a static site generator has a small problem: The website is static! So there’s no process running to notice that some data that was in the future is now in the past. The static site generator says basically, “Hugo, what should the site look like right now,” and then Hugo writes that state to disk as HTML files and other assets, and those get served to the browser.

But Hugo does have one feature that will help: By default, Hugo posts have a date field in their front matter. If that date is in the future when hugo is invoked, the post isn’t put in the output directory, but once the date’s in the past it is. So I have part of a solution right away. I can just dates that are in the future in the front matter, and that gets me almost there.

I say “almost” there because Hugo only looks at this date when it’s running, and it only runs when the site builds, and right now that only happens when I do a git push. So we could have the following scenario: I write a cool blog post, future date it, and push it. Hugo runs, sees that the date is in the future, and declines to render the post. Then I get busy and don’t blog for a month. Now hugo hasn’t had a chance to run since the future date passed, so the post doesn’t get generated until I start blogging again. What I get out of the box isn’t, “publish this post in the future,” but rather, “publish this post the next time I run git push after the 13th of March.”

I’ve built a workaround for this, which I’m not in love with, but does the job. Here’s the workaround:

  1. Ask Amplify to generate an Incoming Webhook that triggers a build.
  2. Write a lambda that calls the webhook
  3. Write a CloudWatch Events / EventBridge cron rule that triggers the lambda.

When you go to create a Lambda in the console, there’s a template called “Make an HTTPS request” that uses Node. That’s probably the ticket. I modified the example code just a little, since I only ever want to call one endpoint, rather than have request details passed in to the event. Here’s the lambda code:

const https = require('https');

/**
 * Trigger a build of the blog so scheduled posts get to be created.
 */
exports.handler = (event, context, callback) => {
    const options = {
        hostname: 'webhooks.amplify.us-east-1.amazonaws.com',
        port: 443,
        path: process.env.WEBHOOK,
        headers: {
          'Content-Type': 'application/json'
        },
        method: 'POST'
    }
    const req = https.request(options, (res) => {
        let body = '';
        console.log('Status:', res.statusCode);
        console.log('Headers:', JSON.stringify(res.headers));
        res.setEncoding('utf8');
        res.on('data', (chunk) => body += chunk);
        res.on('end', () => {
            console.log('Successfully processed HTTPS response');
            // If we know it's JSON, parse it
            if (res.headers['content-type'] === 'application/json') {
                body = JSON.parse(body);
            }
            callback(null, body);
        });
    });
    req.on('error', callback);
    console.log('Sending {} to trigger build');
    req.write(JSON.stringify({}));
    req.end();
};

And honestly, the most annoying thing to get right was the cron expression, which looks like this: cron(5 4 * * ? *), which I hope means “run at 5 minutes past 4 AM UTC every day.”

As I’ve said, I’m not in love with this workaround, but it will work, and didn’t take too long to set up. I’ve scheduled the post via this method, so if you’re reading this, I guess it worked.

Till next week, happy learning!
– Will

Comments

Note: recently submitted comments may not be visible yet; the approval process is manual. Please be patient, and check back soon!

Join the conversation!