This article has been updated to the latest version of Angular 8 and tested with Angular 7. The content is likely be applicable for older Angular 2 or other previous versions.

The Angular CLI provides all the built tools out of the box to create, build, and test Angular applications. In this post, we will build a continuous integration (CI) build system using Github Actions.

Our CI process will run our build and unit tests anytime we push to our repository. Using the new Github Actions feature, we can easily integrate a CI system into our Github repositories. Let’s get started!

Running Angular Unit tests with Puppeteer

In our Angular CLI projects, we can build and test our entire project with just a few commands. To run these commands in our Github Actions environment, we need to add a new dependency called Puppeteer.

Puppeteer is a headless version of Chrome. A “headless” browser is a browser that does not have a visual GUI. Using Puppeteer we can run Chrome in a CI environment, which allows us to run our unit tests. To install Puppeteer to our project, we can run the following command.

npm install Puppeteer --save-dev

Now that we have Puppeteer installed, we need to add a new configuration option to the karma.conf.js file. This new option config allows our Github Actions to run our tests with Puppeteer.

// karma.conf.js
restartOnFileChange: true,
restartOnFileChange: true,
customLaunchers: {
  ChromeHeadlessCustom: {
    base: 'ChromeHeadless',
    flags: ['--no-sandbox', '--disable-gpu']
  }
},

In our package.json file we are going to add a few new commands that will make it easier to run our CLI build.

// package.json
"scripts": {
  "ng": "ng",
  "clean": "rimraf ./dist",
  "start": "ng serve",
  "build": "ng build",
  "build:prod": "ng build --prod",
  "test": "ng test --watch=false --browsers=ChromeHeadlessCustom",
  "test:watch": "ng test",
  "lint": "ng lint",
  "e2e": "ng e2e",
  "build:ci": "npm run clean && npm run test && npm run build:prod"
},

With our new command build:ci, we can clean, run, and build our Angular CLI project with a single command. Our updated test script now runs our unit tests once and sets the appropriate browser configuration, ChromeHeadlessCustom, from our karma.conf.js file.

Integrating the Angular CLI with Github Actions

Now that we can run our unit tests in Puppeteer we can integrate our project into Github Actions. We can run CI builds by creating build scripts or “Actions” in our project. To create actions, we need to create a new directory .github/workflows/.

In our directory, we can create many new build actions, but for now, we have a single build.yml file.

name: Build
on:
  push:
    branches:
      - master

jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [12.x]

    steps:
      - uses: actions/checkout@v1

      - name: Node $
        uses: actions/setup-node@v1
        with:
          node-version: $

      - name: npm install and npm run build
        run: |
          npm ci
          npm run build:ci 

In our build.yml we define an action name as well as a trigger of when it should run. In this example, our action runs whenever we push to the master branch. You can configure this to run more complex scenarios like pull requests and branches. Once we define a trigger, we can define our jobs we want to run. We run our jobs using a Ubuntu Linux environment. We define our NodeJS we want to run.

Once we have our environment configured, we can define our build steps. In our example, we log out the Node version that is running then install the NPM packages for our project. Once install has completed, we then run the npm run build:ci command to build and test our project.

While this config is functional, it is slow having to reinstall our packages with each build. We can optimize our build speed by caching our NPM node_modules.


name: Build
on:
  push:
    branches:
      - master

jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [12.x]

    steps:
      - uses: actions/checkout@v1

      - name: Cache node modules
        uses: actions/cache@v1
        with:
          path: ~/.npm
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-

      - name: Node ${{ matrix.node-version }}
        uses: actions/setup-node@v1
        with:
          node-version: ${{ matrix.node-version }}

      - name: npm ci and npm run build
        run: |
          npm ci
          npm run build:ci 

We add a new step to our job that tracks our package-lock.json for any changes. If the job doesn’t see any differences, then it can use the npm cache from a previous build, which speeds things up quite a bit.

Now if we push our project, we can go to the Actions tab in the Github Repo webpage to see the status of our build.

Example of building a Angular CLI project with Github Actions

We can also add a build status badge to our README.md project file to easily see a project build status.

Angular CLI build status badge with Github Actions

To add a badge to your README.md use the following syntax:

[![build status](https://github.com/coryrylan/angular-github-actions/workflows/Build/badge.svg)](https://github.com/coryrylan/angular-github-actions/actions)

Using the Angular CLI and Github Actions, it has never been easier to create and test frontend Web apps. Check out the fulling working demo repo below as well as the current build status for the demo!

View Demo Code on Github