Mastering web performance audits with Github Actions & LightHouse

Why performance matters?

Pinterest

It's not just about $

It's also about user experience

Page performance overview as seen in Lighthouse

Is it important to analyze the performance often?

Everything in today's world is automated

Having automated performance audits with CI/CD will help you

  • Analyze performance before the release is shipped to the end-users
  • Make sure most of your users get the best experience out of your products

LightHouse

Lighthouse

An open-source, automated tool for analyzing and improving the quality of web pages. It provides audits for performance, accessibility, progressive web apps, SEO and more.

Lighthouse in Chrome Devtools

  • Download Chrome
  • Go to the URL you want to audit
  • Open Chrome Devtools
  • Click the Audits tab
  • Perform and run Audit

Lighthouse as a node command line tool

npm install -g lighthouse

Lighthouse as a Chrome Extension

Lighthouse as a Chrome Extension

Lighthouse with CI/CD

GitHub Actions

Provides an automated workflow

Allows adding merge checks

name: "build-test"
on: # rebuild any PRs and main branch changes
  pull_request:
  push:
    branches:
      - master
      - 'releases/*'

jobs:
  build_and_test_action: # make sure build/ci work properly
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v1
    - run: |
        npm install
        npm run all
  test_demo_app: # run the demo app's unit tests
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v1
    - run: |
        npm install
        npm run test:ci
      working-directory: './jokester'

Example workflow

What do we do
exactly?

In this talk we're going to

  • Assert that the first contentful paint metric is  less than 4 seconds
  • Assert that the score for Performance and Accessibility is 90% or above
  • Assert the score for SEO is 100%. No compromises there 😎
  • Assert the app is a PWA and is installable
  • Assert the app is available offline (PWA)
  • Assert the app registers a Service Worker (PWA)

Let's get started!

A fresh Angular v10 project

Angular v10 App

Opening Devtools & Starting Audit

Opening Devtools & Starting Audit

Let's run a PROD build

  • npm i -g http-server
  • ng build --prod
  • http-server ./dist/ng-github-lighthouse-ci

Setting up LighthouseCI with Github Actions

Create a workflow YAML file in your project

Create a workflow YAML file in your project

  • create a folder named .github
  • create another folder inside .github folder, named workflows
  • create a file inside the workflows folder, named 'main.yaml'

Create a workflow YAML file in your project

name: CI
on:
  push:
    branches:
      - master
  pull_request:
    branches:
      - master
jobs:
  lhci:
    name: Lighthouse
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Use Node.js 10.x
        uses: actions/setup-node@v1
        with:
          node-version: 10.x
      - name: npm install, build
        run: |
          npm install
          npm run build:prod
      - name: run Lighthouse CI
        run: |
          npm install -g @lhci/cli@0.4.x
          lhci autorun
 

Create a workflow YAML file in your project

name: CI
on:
  push:
    branches:
      - master
  pull_request:
    branches:
      - master
jobs:
  lhci:
    name: Lighthouse
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Use Node.js 10.x
        uses: actions/setup-node@v1
        with:
          node-version: 10.x
      - name: npm install, build
        run: |
          npm install
          npm run build:prod
      - name: run Lighthouse CI
        run: |
          npm install -g @lhci/cli@0.4.x
          lhci autorun
 

Create a workflow YAML file in your project

name: CI
on:
  push:
    branches:
      - master
  pull_request:
    branches:
      - masterjobs:
  lhci:
    name: Lighthouse
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Use Node.js 10.x
        uses: actions/setup-node@v1
        with:
          node-version: 10.x
      - name: npm install, build
        run: |
          npm install
          npm run build:prod
      - name: run Lighthouse CI
        run: |
          npm install -g @lhci/cli@0.4.x
          lhci autorun
 

Create a workflow YAML file in your project

name: CI
on:
  push:
    branches:
      - master
  pull_request:
    branches:
      - master
jobs:
  lhci:
    name: Lighthouse
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Use Node.js 10.x
        uses: actions/setup-node@v1
        with:
          node-version: 10.x
      - name: npm install, build
        run: |
          npm install
          npm run build:prod
      - name: run Lighthouse CI
        run: |
          npm install -g @lhci/cli@0.4.x
          lhci autorun
 

Create a workflow YAML file in your project

name: CI
on:
  push:
    branches:
      - master
  pull_request:
    branches:
      - master
jobs:
  lhci:
    name: Lighthouse
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Use Node.js 10.x
        uses: actions/setup-node@v1
        with:
          node-version: 10.x
      - name: npm install, build
        run: |
          npm install
          npm run build:prod
      - name: run Lighthouse CI
        run: |
          npm install -g @lhci/cli@0.4.x
          lhci autorun
 

Create a workflow YAML file in your project

name: CI
on:
  push:
    branches:
      - master
  pull_request:
    branches:
      - master
jobs:
  lhci:
    name: Lighthouse
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Use Node.js 10.x
        uses: actions/setup-node@v1
        with:
          node-version: 10.x
      - name: npm install, build
        run: |
          npm install
          npm run build:prod
      - name: run Lighthouse CI
        run: |
          npm install -g @lhci/cli@0.4.x
          lhci autorun
 

Create a workflow YAML file in your project

name: CI
on:
  push:
    branches:
      - master
  pull_request:
    branches:
      - master
jobs:
  lhci:
    name: Lighthouse
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Use Node.js 10.x
        uses: actions/setup-node@v1
        with:
          node-version: 10.x
      - name: npm install, build
        run: |
          npm install
          npm run build:prod
      - name: run Lighthouse CI
        run: |
          npm install -g @lhci/cli@0.4.x
          lhci autorun
 

build:prod command in package.json

Create lighthouserc.js at the root of your project

module.exports = {
  ci: {
    collect: {
      numberOfRuns: 3,
      staticDistDir: "./dist/ng-github-lighthouse",
    },
    assert: {
      assertions: {
        "first-contentful-paint": ["warn", { maxNumericValue: 4000 }],
      },
    },
    upload: {
      target: "temporary-public-storage",
    },
  },
};

Create lighthouserc.js at the root of your project

module.exports = {
  ci: {
    collect: {
      numberOfRuns: 3,
      staticDistDir: "./dist/ng-github-lighthouse",
    },
    assert: {
      assertions: {
        "first-contentful-paint": ["warn", { maxNumericValue: 4000 }],
      },
    },
    upload: {
      target: "temporary-public-storage",
    },
  },
};

Create lighthouserc.js at the root of your project

module.exports = {
  ci: {
    collect: {
      numberOfRuns: 3,
      staticDistDir: "./dist/ng-github-lighthouse",
    },
    assert: {
      assertions: {
        "first-contentful-paint": ["warn", { maxNumericValue: 4000 }],
      },
    },
    upload: {
      target: "temporary-public-storage",
    },
  },
};

Create lighthouserc.js at the root of your project

module.exports = {
  ci: {
    collect: {
      numberOfRuns: 3,
      staticDistDir: "./dist/ng-github-lighthouse",
    },
    assert: {
      assertions: {
        "first-contentful-paint": ["warn", { maxNumericValue: 4000 }],
      },
    },
    upload: {
      target: "temporary-public-storage",
    },
  },
};

Upon creating the Pull Request, the Github actions will trigger the pipelines

Upon creating the Pull Request, the Github actions will trigger the pipelines

You can check individual build step

You can check individual build step

Implementing the Full-Fledged Audit

Update the lighthouserc.js

module.exports = {
  ci: {
    collect: {
      numberOfRuns: 3,
      staticDistDir: "./dist/ng-github-lighthouse-ci",
    },
    assert: {
      assertions: {
        "first-contentful-paint": ["warn", { maxNumericValue: 4000 }],
      },
    },
    upload: {
      target: "temporary-public-storage",
    },
  },
};

Update the lighthouserc.js

module.exports = {
  ci: {
    collect: {
      numberOfRuns: 3,
      staticDistDir: "./dist/ng-github-lighthouse-ci",
    },
    assert: {
      assertions: {
        "first-contentful-paint": ["warn", { maxNumericValue: 4000 }],
        "categories:performance": ["warn", { minScore: 0.9 }],
        "categories:accessibility": ["error", { minScore: 0.9 }],
        "categories:seo": ["error", { minScore: 1 }],
        "installable-manifest": "on",
        "offline-start-url": "on",
        "service-worker": "on",
        "works-offline": "on",
      },
    },
    upload: {
      target: "temporary-public-storage",
    },
  },
};

Creating a PR triggers the pipeline

Which is good! And bad!

Let's improve our web app

Turning our Angular app into a PWA

ng add @angular/pwa

  • Adds the @angular/service-worker package to the project.
  • Enables service worker in the app.
  • Updates the index.html file by including a link to add the manifest.webmanifest file.
  • Adds meta tags for theme-color.
  • Installs icon files to support the installed Progressive Web App (PWA).
  • Creates the service worker configuration file called ngsw-config.json, which specifies the caching behaviors and other settings.

ng add @angular/pwa

module.exports = {
  ci: {
    collect: {
      numberOfRuns: 3,
      staticDistDir: "./dist/ng-github-lighthouse-ci",
    },
    assert: {
      assertions: {
        "first-contentful-paint": ["warn", { maxNumericValue: 4000 }],
        "categories:performance": ["warn", { minScore: 0.9 }],
        "categories:accessibility": ["error", { minScore: 0.9 }],
        "categories:seo": ["error", { minScore: 1 }],
        "installable-manifest": "on",
        "offline-start-url": "on",
        "service-worker": "on",
        "works-offline": "on",
      },
    },
    upload: {
      target: "temporary-public-storage",
    },
  },
};

What about SEO?

ng add @angular/pwa

module.exports = {
  ci: {
    collect: {
      numberOfRuns: 3,
      staticDistDir: "./dist/ng-github-lighthouse-ci",
    },
    assert: {
      assertions: {
        "first-contentful-paint": ["warn", { maxNumericValue: 4000 }],
        "categories:performance": ["warn", { minScore: 0.9 }],
        "categories:accessibility": ["error", { minScore: 0.9 }],
        "categories:seo": ["error", { minScore: 1 }],
        "installable-manifest": "on",
        "offline-start-url": "on",
        "service-worker": "on",
        "works-offline": "on",
      },
    },
    upload: {
      target: "temporary-public-storage",
    },
  },
};

Adding meta description tag

<!-- <project-root>/src/index.html -->

<meta name="Description" content="A sample web app to demonstrate Github Actions and LightHouseCI">

Adding rel="canonical"

<!-- <project-root>/src/index.html -->

<link rel="canonical" href="/ng-github-lighthouse-ci"/>

Adding a robots.txt file

# Group 1
User-agent: Googlebot
Disallow: /nogooglebot/

# Group 2
User-agent: *
Allow: /

Adding a robots.txt file

And...!

SUCCESS!!!

Thank You !

Senior Software Engineer

Klarna

Mastering web performance audits with Github Actions & LightHouse

By Ahsan Ayaz

Mastering web performance audits with Github Actions & LightHouse

  • 34
Loading comments...

More from Ahsan Ayaz