Skip to main content

Angular CI/CD Pipeline

Introduction

Continuous Integration and Continuous Deployment (CI/CD) is a modern software development practice that allows developers to frequently merge code changes into a central repository, after which automated builds and tests are run. When properly implemented, a CI/CD pipeline helps teams deliver updates more frequently and reliably. For Angular applications, a CI/CD pipeline can significantly streamline your development workflow and reduce the time spent on manual tasks.

In this guide, we'll explore how to set up a CI/CD pipeline for your Angular applications, understand the benefits, and learn some best practices for implementing an effective pipeline.

What is a CI/CD Pipeline?

A CI/CD pipeline is an automated workflow that helps developers integrate code changes more frequently and reliably. The pipeline consists of two main components:

  1. Continuous Integration (CI): Automatically building and testing code changes to ensure they integrate well with the existing codebase.
  2. Continuous Deployment (CD): Automatically deploying approved changes to production environments.

For Angular applications, a CI/CD pipeline typically includes:

  • Code linting
  • Building the application
  • Running unit tests
  • Running end-to-end tests
  • Deploying to staging or production environments

Benefits of Implementing a CI/CD Pipeline for Angular

  • Faster feedback: Developers receive immediate feedback on their code changes
  • Reduced manual errors: Automation eliminates human errors during deployment
  • Consistent environments: Pipeline ensures code is tested in the same environment before deployment
  • Improved collaboration: Team members can easily see and review code changes
  • Higher code quality: Automated testing ensures that only working code is deployed
  • Faster release cycles: Automating processes speeds up the time to market

Setting Up Your First CI/CD Pipeline for Angular

Let's explore how to set up a basic CI/CD pipeline using GitHub Actions, one of the most popular CI/CD services that's free for public repositories.

Step 1: Create a GitHub Actions Workflow File

In your Angular project, create a directory structure in the root of your project:

.github/
└── workflows/
└── main.yml

The main.yml file will contain your workflow configuration:

name: Angular CI/CD

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
build-and-test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2

- name: Set up Node.js
uses: actions/setup-node@v2
with:
node-version: '16'

- name: Install dependencies
run: npm ci

- name: Lint
run: npm run lint

- name: Build
run: npm run build --if-present

- name: Test
run: npm run test -- --watch=false --browsers=ChromeHeadless

This basic workflow will:

  1. Trigger on push to the main branch or on pull requests to the main branch
  2. Set up a Node.js environment
  3. Install dependencies
  4. Run linting
  5. Build the Angular application
  6. Run tests in headless Chrome

Step 2: Add Deployment to the Pipeline

To add deployment capabilities to your pipeline, you'll need to extend the configuration:

name: Angular CI/CD

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
build-and-test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2

- name: Set up Node.js
uses: actions/setup-node@v2
with:
node-version: '16'

- name: Install dependencies
run: npm ci

- name: Lint
run: npm run lint

- name: Build
run: npm run build --if-present

- name: Test
run: npm run test -- --watch=false --browsers=ChromeHeadless

- name: Archive build
if: github.event_name == 'push'
uses: actions/upload-artifact@v2
with:
name: dist
path: dist

deploy:
if: github.event_name == 'push'
needs: build-and-test
runs-on: ubuntu-latest

steps:
- name: Download build
uses: actions/download-artifact@v2
with:
name: dist
path: dist

- name: Deploy to Firebase
uses: w9jds/firebase-action@master
with:
args: deploy --only hosting
env:
FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}

This extended workflow adds:

  1. An archiving step to save the build output
  2. A separate deployment job that depends on the build job
  3. A deployment step using Firebase Hosting (a popular hosting service for Angular applications)

Step 3: Setting Up Environment Variables and Secrets

For the deployment to work, you need to set up the necessary secrets:

  1. In your GitHub repository, go to "Settings" > "Secrets" > "New repository secret"
  2. Create a new secret named FIREBASE_TOKEN with your Firebase CI token value

For Firebase deployment, you would also need to have:

  • Firebase CLI installed globally: npm install -g firebase-tools
  • A Firebase project set up
  • Firebase initialized in your project: firebase init
  • A Firebase token: firebase login:ci

Advanced CI/CD Pipeline Features

Once you have a basic pipeline working, you might want to enhance it with these features:

1. Environment-specific builds

- name: Build for production
if: github.ref == 'refs/heads/main'
run: npm run build -- --configuration=production

- name: Build for staging
if: github.ref == 'refs/heads/develop'
run: npm run build -- --configuration=staging

2. Running end-to-end tests

- name: E2E Tests
run: npm run e2e -- --configuration=ci

3. Code coverage reporting

- name: Test with coverage
run: npm run test -- --no-watch --code-coverage

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v2
with:
token: ${{ secrets.CODECOV_TOKEN }}
directory: ./coverage/
fail_ci_if_error: true

4. Caching dependencies

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

Real-World Example: Complete Angular CI/CD Pipeline

Here's a comprehensive example of a CI/CD pipeline for an enterprise Angular application:

name: Angular Enterprise CI/CD

on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2

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

- name: Set up Node.js
uses: actions/setup-node@v2
with:
node-version: '16'

- name: Install dependencies
run: npm ci

- name: Lint
run: npm run lint

- name: Check formatting
run: npm run format:check

- name: Run unit tests
run: npm run test -- --no-watch --code-coverage --browsers=ChromeHeadless

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v2

- name: Build for production
if: github.ref == 'refs/heads/main'
run: npm run build -- --configuration=production

- name: Build for staging
if: github.ref == 'refs/heads/develop'
run: npm run build -- --configuration=staging

- name: Archive build
if: success() && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop')
uses: actions/upload-artifact@v2
with:
name: dist
path: dist

e2e:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2

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

- name: Set up Node.js
uses: actions/setup-node@v2
with:
node-version: '16'

- name: Install dependencies
run: npm ci

- name: Run E2E tests
run: npm run e2e -- --configuration=ci

deploy-staging:
if: success() && github.ref == 'refs/heads/develop'
needs: [build, e2e]
runs-on: ubuntu-latest

steps:
- name: Download build
uses: actions/download-artifact@v2
with:
name: dist
path: dist

- name: Deploy to Firebase staging
uses: w9jds/firebase-action@master
with:
args: deploy --only hosting:staging
env:
FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}

deploy-production:
if: success() && github.ref == 'refs/heads/main'
needs: [build, e2e]
runs-on: ubuntu-latest
environment: production

steps:
- name: Download build
uses: actions/download-artifact@v2
with:
name: dist
path: dist

- name: Deploy to Firebase production
uses: w9jds/firebase-action@master
with:
args: deploy --only hosting:production
env:
FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}

This advanced example includes:

  • Separate build configurations for staging and production
  • Code coverage reporting
  • End-to-end tests in a separate job
  • Different deployment targets based on the branch
  • Environment protection for production deployment

Integrating with Other CI/CD Services

While we've focused on GitHub Actions, there are many other CI/CD services you can use:

GitLab CI/CD

Create a .gitlab-ci.yml file in your project root:

image: node:16

stages:
- build
- test
- deploy

cache:
paths:
- node_modules/

build:
stage: build
script:
- npm ci
- npm run build
artifacts:
paths:
- dist/

test:
stage: test
script:
- npm ci
- npm run lint
- npm run test -- --no-watch --browsers=ChromeHeadless

deploy:
stage: deploy
script:
- npm install -g firebase-tools
- firebase deploy --token $FIREBASE_TOKEN
only:
- main

CircleCI

Create a .circleci/config.yml file:

version: 2.1
jobs:
build-and-test:
docker:
- image: cimg/node:16.13
steps:
- checkout
- restore_cache:
keys:
- v1-dependencies-{{ checksum "package.json" }}
- run: npm ci
- save_cache:
paths:
- node_modules
key: v1-dependencies-{{ checksum "package.json" }}
- run: npm run lint
- run: npm run build
- run: npm run test -- --no-watch --browsers=ChromeHeadless

deploy:
docker:
- image: cimg/node:16.13
steps:
- checkout
- restore_cache:
keys:
- v1-dependencies-{{ checksum "package.json" }}
- run: npm ci
- run: npm run build
- run: npm install -g firebase-tools
- run: firebase deploy --token "$FIREBASE_TOKEN"

workflows:
version: 2
build-test-deploy:
jobs:
- build-and-test
- deploy:
requires:
- build-and-test
filters:
branches:
only: main

Best Practices for Angular CI/CD Pipelines

  1. Keep builds fast: Optimize your tests and build process to run as quickly as possible
  2. Use caching: Cache dependencies to speed up builds
  3. Implement different environments: Set up separate environments for development, staging, and production
  4. Use environment variables: Store sensitive information like API keys in environment variables
  5. Run all tests: Make sure unit, integration, and end-to-end tests are part of your pipeline
  6. Implement code quality checks: Add linting, formatting, and other code quality checks
  7. Version your deployments: Use versioning for your deployments to enable rollbacks
  8. Monitor deployments: Implement monitoring to detect issues quickly
  9. Implement approval workflows: Require approvals for production deployments
  10. Document your pipeline: Keep documentation updated on how the pipeline works and how to troubleshoot issues

Troubleshooting Common CI/CD Issues

1. Build failing due to dependency issues

Solution: Make sure to use exact versions in your package.json or use lockfiles (package-lock.json or yarn.lock).

2. Tests failing in CI but passing locally

Solution: Ensure your test environment in CI matches your local environment as closely as possible. Use headless browsers for testing.

- name: Test
run: npm run test -- --no-watch --browsers=ChromeHeadless

3. Deployment failing due to permissions

Solution: Check that your CI service has the correct permissions and API tokens to deploy.

4. Long build times

Solution: Implement caching and optimize your build process.

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

Summary

Setting up a CI/CD pipeline for your Angular application can significantly improve your development workflow by automating testing, building, and deployment processes. We've covered:

  • What a CI/CD pipeline is and its benefits for Angular applications
  • How to set up a basic CI/CD pipeline using GitHub Actions
  • How to implement advanced features in your pipeline
  • A real-world example of a complete CI/CD pipeline
  • Integration with other CI/CD services
  • Best practices and troubleshooting tips

By implementing these practices, you'll experience faster feedback cycles, reduced manual errors, and more reliable deployments, ultimately leading to higher-quality Angular applications.

Additional Resources

Exercises

  1. Set up a basic GitHub Actions workflow for an existing Angular project.
  2. Extend your pipeline to deploy to Firebase hosting.
  3. Implement environment-specific builds (development, staging, production).
  4. Add code coverage reporting to your pipeline.
  5. Optimize your pipeline by implementing caching and parallel jobs.

Happy coding and deploying!

💡 Found a typo or mistake? Click "Edit this page" to suggest a correction. Your feedback is greatly appreciated!