Automatically deploying my blog with Travis
I configured Travis to test and deploy my blog every time I push new code to my master branch on Github.
I’ve gotten my blog deployed on Firebase. Now I can deploy changes as easily as running firebase deploy
from the terminal. But I would prefer publish changes when I push to Github. Or, rather, I want to consider master
on Github as the latest stable version — this is the code that needs to be live on Firebase.
I need automated deployments. This is something that Travis does very well.
Travis is a continuous integration tool. It watches your Github repository and tries to build and test your code every time you commit a change. It works well for public and private repositories. It’s free for public repositories.
They aren’t alone in this space. Many companies use Jenkins internally. Other notable competitors are Codeship and Circle CI. There are likely dozens more. You could probably build your own service on your development laptop using Docker in about day (if you had a tutorial to follow).
We’re using Travis for this project because it is the most obvious choice. Many popular open source projects use Travis, likely because their free tier is open source friendly. They support Github public repos for free specifically to support open source software developers. In a future project I intend to try out a competitor. For now, we’re going with Travis.
Let’s go with Travis
You need an account. I already had one. You have to give them permission to see your Github account — this gives them broad access. Travis allows you to choose the repositories you want watched.
The default settings on Travis are just fine. There are very few settings — mostly for experimental features — so it’s not really necessary at all. We will be adding an environment variable a little later.
One of the annoying things about Travis is that there isn’t a “go” button. It will only start when a new commit is made to your master branch on Github. This can make it difficult to get started and test your initial configuration.
- Sign up for Travis (getting started)
- Give Travis access to your Github
- Activate Travis for your repo
- Make a test commit to kick off the build and deploy
Configure travis
In order to make a deploy, we need to make a commit. We’re going to commit a travis config file to kick off our first automated build.
Travis offers almost no configuration online because you are supposed to add a .travis.yml
file to your project. This file is pretty easy to make but it can get quite complicated. Under the hood, Travis is is standing up a brand new virtual machine for your project, running a fresh build from the latest code on Github, and running tests.
By default, Travis is designed to test your code, not deploy it. Thankfully for us, you can also configure a deploy step that runs after the tests.
Travis is designed for pretty serious teams that need strict and predictable build environments. Projects with a large audience need to ensure that the code on their website is the right code, and that it passes at least some kind of test.
In some ways, Travis and it’s competitors are overkill for a little blog like mine. But, it’s free and offers a feature we’re after — automatic deployments of Github master to Firebase.
Fix our tests
For now, we won’t need to run any tests. This is a boilerplate Gatsby blog and most of the testable features live within Gatsby somewhere. We’re presuming that Gatsby is testing itself well enough that we don’t have to.
There are probably some rudimentary tests that we should run to ensure that our latest code isn’t totally broken. However, that is outside the scope of what we’re trying to accomplish right now. Without tests, we are making the bold assumption that the code we commit to master will build correctly. For the case of a personal Gatsby blog, the tests can wait for another day.
Gatsby ships with a test script that essentially throws an error stating we should add some tests. Failing tests will prevent Travis from deploying our project. So we’re going to have to soften that restriction. We’ll change the message from “Error” to “Warning” and change the exit code from “1” to “0”.
Edit the “test” script in your package.json
:
{
"scripts": {
"test": "echo \"Warning: no test specified\" && exit 0"
}
}
Of course, this isn’t a permanent solution. At the very least we should run the lint
command that’s included with Gatsby. But that can wait (we’ll enable linting next). Right now we need to get Travis to deploy our blog. We’ve set our tests to emit a warning about “no tests” and exit with a success code so that Travis will continue.
NOTE: I broke my build while working on this post because my YAML frontmatter had a syntax error — frontmatter errors make Gatsby unhappy. For me the problem was that I wasn’t wrapping a string in quotes. In travis this showed up as a “failed” build. This kept the project from deploying because the build step was failing. In that way, building the project is a good minimal test.
Add a prod build script
Gatsby comes with a build script and a deploy script. We’re going to be manually deploying with a firebase deploy
command in our .travis.yml
. But before we can deploy, we need to build. So we’ll copy the build step from the existing deploy script and call it deploy:prod
.
Add these relevant build and deploy scripts to your package.json
:
{
"scripts": {
"test": "echo \"Warn: no test specified\" && exit 0",
"build": "gatsby build",
"build:prod": "yarn build -- --prefix-links",
"deploy": "yarn build:prod && firebase deploy"
}
}
Adding a .travis.yml
We need to add a .travis.yml
file for building and deploying our project.
My Gatsby project was generated with a Travis file but it is for building Gatsby itself, not our project. We can get by with a much simpler configuration. I ended up having to follow these instructions because the Firebase deployment documentation provided by Travis didn’t seem to work (at the time this was written).
NOTE: I kept getting an error during the deploy phase when following the official docs. The error, Error: Specified public directory does not exist, can't deploy hosting
, was cryptic enough that I decided to write my deployment manually.
NOTE: I was getting a different error with the manual deploy below because of an issue with firebase-tools on Travis. I fixed it by installing firebase-tools directly from master. You might not need to do that if you’re living in the future.
NOTE: I had to follow these instructions because I’m using Node 8 and it requires a more recent compiler to install native extensions like node-sass.
Here’s the .travis.yml
file I ended up with:
language: node_js
node_js:
- "8"
cache: yarn
addons:
apt:
packages:
- g++-4.8
sources:
- ubuntu-toolchain-r-test
env:
CXX=g++-4.8
branches:
only:
- master
before_script:
# If you're have troubles with firebase/firebase-tools#382
# - "yarn global add https://github.com/firebase/firebase-tools"
- "yarn global add firebase-tools"
- "yarn global add gatsby"
script:
- "yarn test"
- "yarn build:prod"
after_success:
- "firebase deploy --token=${FIREBASE_API_TOKEN}"
Add firebase deploy token for Travis
In order for Travis to deploy on your behalf you need to generate a Firebase CI token for use with Travis.
Running the following script will generate a token for you. First it forces you to log in, then it will output a token in the terminal. This should be a secret token — anyone who knows your token can deploy as you.
firebase login:ci
# if you accidentally share your token with someone you don't trust, destroy it
firebase logout --token="my untrusted token"
For now, go to your project in Travis and add the token as a environment variable named FIREBASE_API_TOKEN
. This is outlined in more detail in these instructions or in the Travis environment variable docs. Make sure you leave “display value in build log” off.
If you’re feeling adventurous, you can put this deploy token in your .travis.yml
file as an encrypted secret. This would not give your secret away but it would allow anyone with access to your Github to view the encrypted version of your secret. Given that this is a public repository, I’m choosing to store it in my online Travis settings for now.
# only if you're adventurous... everyone will be able to see your encrypted token
# probably better to just add the token to your Travis environment variable settings online
travis encrypt FIREBASE_API_TOKEN="the generated token" --add env
Making a test commit
With all of this in place, we’re finally ready to push a change to our Github master.
git add .
git commit -m "Adding travis config"
git push origin master
At this point, Travis should pick up your changes, test, build and deploy your project. If you have any troubles, you will need to push changes to your master branch to trigger a new travis build.