Under Engineered

Crafting a PWA: Part 1 — Setting up the development workflow

PWAs have been gaining popularity for quite sometime now. Crafting a good, performant experience is continuous journey.

So before even embarking on the PWA journey, we should invest time on setting up the development workflow.

This separates great apps from apps that were great once. 🔗

For example let’s consider a GitHub repository which has Hacker News front end implemented. This is not a PWA yet. It’s made with React and Redux.

I want every Pull Request on this repository from now on to be tested and audited for performance issues.

How do I do that? Using a CI server, like Travis. With Travis we’ll add support for lint checks, automatic stage deployments, audit using lighthouse and would be in absolute control over the changes happening on the app at all times.

Step 1 — Adding Travis CI 🔗

Enable Travis CI for your repository by going to your profile page


Once you enable the switch, you’d then need to add a .travis.yml which will instruct Travis on what to do at different stages of the build.

language: node_js
- "7"
script: npm run lint
- "node_modules"

Above is a simple .travis.yml which runs lint checks on PR.

Step — 2: Adding stage deployments 🔗

  1. Being able to see the code changes in action helps the reviewer a lot to merge your changes with confidence.

  2. I’ll use Now to deploy your code to a unique stage URL once a Pull Request is created. You can use Surge as well.

  3. Our challenge is to integrate Now deployments from Travis so that anytime a PR is created/updated the new changes are deployed and ready to be seen/audited by other reviewers.

  4. now-travis is an excellent utility to deploy to now. The thing with now deployments is — Every time you deploy a project, now will provide you with a new, unique URL.

  5. But now-travis doesn’t provide the ability to save the URL so that we can use it later to run lighthouse audits. I’ll cover this bit in a little while.

  6. So I added a change to save the deployed URL to a temporary file and created a Pull Request which hasn’t been merged as of now. You can use this fork for your setup: https://github.com/ankeetmaini/now-travis

  7. npm i -D ankeetmaini/now-travis This will add now-travis as a dev dependency to your project.

  8. Follow the instructions in the README to integrate now deployments with Travis.

  9. now uses npm start or npm run now-start to start your application. It gives preference to now-* commands so in this case now-start would be executed instead of npm start. This is useful because in dev mode also I’ll use npm start and in production I may need to pass NODE_ENV=production. Or you may need to send something else entirely.

  10. Update your .travis.yml to run now-travis after successful build. See this line of code in after_script
    *NOW_ALIAS=react-hn-ankeetmaini node_modules/.bin/now-travis — file=now_url*

language: node_js
  - '7'
script: npm run lint
  - NOW_ALIAS=react-hn-ankeetmaini node_modules/.bin/now-travis --file=now_url
    - master
    - node_modules
    secure: jMH9lqo0E83BhZR8oZiXM...

With this setup now Travis will deploy every pull request¹. You can see in the below image that a Staging deployment was done.

CI checks PR for lint, deploys and audits app performanceCI checks PR for lint, deploys and audits app performance

Step — 3: Integrating Lighthouse 🔗

  1. Lighthouse is an audit tool by Google which checks your app against a number of points and scores it.

  2. It’s vital to run this audit continuously, so as to always keep our app fast and performant. We’ll use lighthouse-ci to integrate it with Travis.

  3. Add lighthousebot as a collaborator in your repository, so that it can update the status of your PR and post a comment with the Lighthouse score.

  4. Request an API key and add an ENV variable in the Travis. This isn’t necessary as of now, but will be done in the future.

  5. Since now deploys our app to a unique URL, we save it in a file named now-url. We need to read this URL from the file and give it as an input to lighthouse-ci.

  6. To do this I created a file run-lighthouse.js at the root of the folder, with the following code. lighthouse-ci takes following options and we’re passing the same in the below file.

#! /usr/bin/env node

const fs = require("fs")
const argv = require("yargs").argv
const childProcess = require("child_process")

const path = require("path")

const fileName = argv.file

const file = fs.readdirSync(__dirname).filter(f => f === fileName)[0]
const nowUrl = fs.readFileSync(file).toString()

// bail, we did not get the URL
if (!nowUrl) process.exit(1)

const lighthouseCi = path.resolve(__dirname, "node_modules", "lighthouse-ci")
const child = childProcess.fork(lighthouseCi, [

child.on("error", err => {
  1. Lastly add an entry into .travis.yml in the after_script section to run the above file after the stage deployment is done. — file is the argument which takes in the file name from which the deployed URL needs to be read. This will now evaluate your stage deployment and fail² the PR if you’ve not passed a minScore and also post a comment with your lighthouse score, see this PR https://github.com/ankeetmaini/react-hn/pull/9

    ./run-lighthouse.js --file=now_url

Congratulations!, you’ve successfully setup an awesome workflow. All the code used in the citations lives here.

[1] since free OSS plan in now can only have three active deployments at a time, you might need to manually remove deployments using now rm id

[2] right now the lightousebot will only post a comment and not fail your PR because of insufficient rights on the repository, the above screenshot which shows failing of my PR is because I’ve run a separate instance of lighthouse-ci for demo purposes. See this issue for more details

Subscribe below to get future posts