A Handy Publish Script for Static Sites Using CloudFront

A Handy Publish Script for Static Sites Using CloudFront

Hold on Cowboy

This blog post is pretty old. Be careful with the information you find in here. It's likely dead, dying, or wildly inaccurate.

You find yourself using S3 for your static frontend code and using CloudFront to serve out that static content. Here is a handy script to deploy and invalidate old versions.

#!/bin/bash

CURR_COMMIT=$(git rev-parse HEAD);
CURR_VERSION=$(node -e "console.log(require('./package.json').version);");
VER_HASH=$(git rev-list -n 1 v$CURR_VERSION);

# Don't want to redo version bump
if [ $CURR_COMMIT == $VER_HASH ]
then
    echo 'Already up to date'
    exit
fi

npm version patch;

NEW_VERSION=$(node -e "console.log(require('./package.json').version);");

echo $NEW_VERSION;

git push origin head;

npm run buildprod

aws s3 sync ./public s3://www.example.com --size-only --delete;

# Invalidate cache
aws cloudfront create-invalidation \
    --distribution-id YOUR_DISTRIBUTION_ID \
    --paths "/*";

What Does It Do?

This script will bump the current version of the project, build your static site, upload to AWS S3 bucket, then tell Cloudfront to invalidate all the files in the specific Cloudfront distribution.

What You’ll Need

To run this you’ll need a few things in place

  1. You need the AWS CLI. You can installed this with Brew brew install awscli, npm npm install -g aws-cli, or just from their site https://aws.amazon.com/cli/
  2. After you have AWS CLI installed, then you need to configure it. This requires putting the file ~/.aws/credentials in place with your creds. Read More
  3. You will need git installed.
  4. For this script we use npm.

Let’s Walk Through Each Part

CURR_COMMIT=$(git rev-parse HEAD);
CURR_VERSION=$(node -e "console.log(require('./package.json').version);");
VER_HASH=$(git rev-list -n 1 v$CURR_VERSION);

Here we are gathering a few details. We want to know the latests commit hash. Then we pull the version from the package.json file and use that to look up the commit hash of the tag. When we use npm version patch, it creates a semver tag like v1.3.1.

# Don't want to redo version bump
if [ $CURR_COMMIT == $VER_HASH ]
then
    echo 'Already up to date'
    exit
fi

Here we check if the tag hash is different than the current HEAD hash. Basically we don’t want to create another commit if nothing new has been committed. You need to actually change code and make a new commit before this script will continue past this part.

npm version patch;

NEW_VERSION=$(node -e "console.log(require('./package.json').version);");

echo $NEW_VERSION;

git push origin head;

Here we tell NPM to bump the patch version up one. It will change the package.json file version, make a commit, then create a git tag with that version. After we bump the version, we retrieve the version from package.json like we did earlier and echo that new version out. Then we push this new commit and tag up to our git server (Github, GitLab, Bitbucket, etc).

npm run buildprod

aws s3 sync ./public s3://www.example.com --size-only --delete;

Here we build our static site files. In this case I’m using NPM to fire off a Webpack configuration to build my files. Then we use the AWS CLI to sync our files to S3. We basically tell it to delete all the files on the server that are not local and only use size as a comparison indicator.

# Invalidate cache
aws cloudfront create-invalidation \
    --distribution-id YOUR_DISTRIBUTION_ID \
    --paths "/*";

After we’ve done uploading our files to S3, we tell Cloudfront to invalidate all our files. Since this is a wild card, we only get dinged for one cache invalidation from Cloudfront rather than the hundreds or thousands of files that are on our site (that could get expensive, since Cloudfront starts charging for invalidations past 1000/month)

Summary

This script makes it really easy for me to just fire and forget and it has a little built in safe check so I’m not just bumping up the version with no new code to show for it. I’ve gotten in the habit to create a publish.sh for many of my projects since I’m not always sure how each are deployed if I only deploy on occasion. Happy Shipping.