Updated 9th December to illustrate latest iteration of Travis.yml
Before I ported this site over to Gatsby, it was powered by good old Jekyll. I'd make an edit, run
jekyll build, then push the output to S3 with
s3_website. Not exactly onerous, but I noticed that I'd be drafting posts, or updating the structure of the site and then not pushing them live in a timely manner. (And what do you know, it's happened again! – Ed., December 2nd)
Cue a Continuous Integration (CI) solution – although it took me many attempts to get it right. And by right, I mean it at least:
- builds the site
- deploys to S3
- invalidates the CloudFront distribution.
Attempt #1: Use the default S3 provider
Travis' documentation is pretty, pretty, pretty good. I think you could just about get away with this, if you're happy to serve straight from the S3 bucket:
language: node_js node_js: - "8" script: - yarn build deploy: provider: s3 access_key_id: $AWS_ACCESS_KEY_ID secret_access_key: $AWS_SECRET_ACCESS_KEY bucket: danmatthew.co.uk region: $AWS_DEFAULT_REGION # Prevent Travis from deleting your built site so it can be uploaded. skip_cleanup: true # Path to a directory containing your built site. local_dir: public on: branch: master
Because I have both
yarn.lock in my repository, Travis is smart enough to figure I want to use Yarn to run during the
I like this implementation, but Travis is only concerned with S3 here. I have a Cloudfront distribution sat in front of this bucket in the hope of saving a few pence and making the site feel snappy. Sure, I could manually log in to AWS and invalidate the distribution or do it via the command line, but y'know: let's automate it.
Attempt #2: Use AWS CLI to invalidate the Cloudfront distribution
This is where it feels messy, and there are a bunch of extra steps to mess up. AWS CLI is installed via Pip, so this implementation needs to be driven by Python.
language: python python: 3.6 sudo: required dist: trusty env: - TRAVIS_NODE_VERSION="8"
I had to explicitly specify the version of Python, because the default appeared to be 2.4 and I ran into issues… Java-related, possibly?.
This is where it starts to be a blend of different tasks, and I'm piecing together instructions from various sources.
before_install: - curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - - echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list - sudo apt-get -qq update - sudo apt-get -y install yarn
As it's not provided by default, I need to install Node and NPM in order to install my site's dependencies and then build it. I figured NVM would be the most sensible approach:
install: - rm -rf ~/.nvm && git clone https://github.com/creationix/nvm.git ~/.nvm && (cd ~/.nvm && git checkout `git describe --abbrev=0 --tags`) && source ~/.nvm/nvm.sh && nvm install $TRAVIS_NODE_VERSION - pip install awscli
I discover afterwards that the ugly
$TRAVIS_NODE_VERSION environment variable could have been avoided by specifying the version in the project's
As the Travis cycle goes
deploy, next up is…
before_script: npm install script: npm run build
(Travis experts – could those commands just have been listed sequentially in the
Configure S3 bucket
We use the same
deploy block as before:
deploy: provider: s3 access_key_id: $AWS_ACCESS_KEY_ID secret_access_key: $AWS_SECRET_ACCESS_KEY bucket: danmatthew.co.uk region: $AWS_DEFAULT_REGION # Prevent Travis from deleting your built site so it can be uploaded. skip_cleanup: true # Path to a directory containing your built site. local_dir: public on: branch: master
Blow away the old Cloudfront distribution
When this succeeds, we can now use the AWS CLI to ensure that Cloudfront serves the latest version of these files:
after_deploy: # Allow `awscli` to make requests to CloudFront. - aws configure set preview.cloudfront true # Invalidate every object in the targeted distribution. - test $TRAVIS_BRANCH = "master" && aws cloudfront create-invalidation --distribution-id $CLOUDFRONT_DISTRIBUTION_ID --paths "/*"
Inspecting the bucket, I notice that Travis doesn't doesn't sync the files – i.e. only new files are added; old ones are deleted. It might not be an issue in practice, but I quite like keeping my buckets tidy… 😒
Attempt #3: Use s3_website
I mentioned in the intro that when publshing the website from my computer I used the s3_website gem. I know it, it does all the tasks I want it to, and it's straightforward to configure.
(Although, I fib: since upgrading to Sierra, I've not been
able inclined to deploy since it requires a Java installation which I really don't want to do. I know, weaksauce.)
So, we go again! Now, we're sacking off a Python-based deployment pipe in favour of a Ruby powered process:
language: ruby rvm: 2.2 sudo: required dist: trusty # Again with the redundant env var env: - TRAVIS_NODE_VERSION="8" before_install: - curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - - echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list - sudo apt-get -qq update - sudo apt-get -y install yarn
install: - . $HOME/.nvm/nvm.sh - nvm install stable - nvm use stable - gem install s3_website
Push to S3
before_script: - yarn script: - yarn build after_script: - test $TRAVIS_BRANCH = "master" && s3_website push
Boom. All my
s3_website settings are configured in environment variables and my
s3_website.yml config, so that one line in the
after_script block pushes to the correct bucket and invalidates the Cloudfront distribution.
Looking back over it, I think I'd be quite happy to sack off the yarn installation and reply on NPM. Again I wonder whether the blocks can be consolidated into one and the commands listed sequentially? I'm not experienced enough with Travis to know what benefits I get from splitting the commands into the different stages, especially since this particular deployment process is not exactly complicated. Perhaps so the process can fail earlier if necessary?
Proposed attempt #4: Use AWS CLI for everything
I suspect I could probably do all of the above with the AWS CLI, though it will need switching back over to Python again. Woe is me 😩.
Attemp #5: Concise
…whereupon I decided to sack off Yarn, and after configuring Travis to build PR and branch merges, whitelist the
branches: only: - master language: ruby rvm: 2.2 sudo: required dist: trusty cache: - npm install: - . $HOME/.nvm/nvm.sh - nvm install stable - nvm use stable - gem install s3_website before_script: - npm install script: - npm run build after_script: - s3_website push