Ship internal tools in hours, not weeks. Real auth, users, jobs, audit logs, and cohesive UI included. Early access $249 $499 → [Get it now]

How to set up CI/CD for Angular

Setting up a robust CI/CD pipeline for Angular is essential for maintaining high-quality code and ensuring fast, reliable deployments. As a developer with over 25 years of experience and the creator of CoreUI, I have designed and optimized countless pipelines for enterprise-scale Angular applications since 2014. The most efficient and modern approach involves using a YAML-based provider like GitHub Actions to automate the entire lifecycle from code push to production deployment. By automating these steps, you ensure that every change is verified against your test suite and coding standards before reaching your users.

Use a GitHub Actions workflow file to automate linting, testing, and building your Angular application on every push to the main branch.

1. Defining the Workflow Trigger and Environment

The first step in setting up CI/CD is defining when the pipeline should run and what environment it requires. We typically trigger these actions on pushes to the main branch or when a pull request is opened. This ensures that the code is always in a deployable state.

name: Angular CI/CD Pipeline

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

jobs:
  build-and-test:
    runs-on: ubuntu-latest
    
    strategy:
      matrix:
        node-version: [ '20.x' ]

This section initializes the workflow named ‘Angular CI/CD Pipeline’. The on key specifies that the job runs on pushes and pull requests to the main branch. We use ubuntu-latest as the virtual environment because it is fast and includes most necessary tools pre-installed. The strategy matrix allows us to test against specific Node.js versions, ensuring compatibility across different environments.

2. Installing Dependencies with Caching

Angular projects often have large node_modules folders. Re-installing these from scratch on every run is inefficient. We use the actions/setup-node and actions/cache features to persist dependencies across pipeline runs, significantly reducing execution time.

    steps:
    - uses: actions/checkout@v4

    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v4
      with:
        node-version: ${{ matrix.node-version }}
        cache: 'npm'

    - name: Install dependencies
      run: npm ci

The checkout action pulls your code into the runner. Then, setup-node configures the environment and enables built-in caching for npm. The npm ci command is preferred over npm install in CI environments because it is faster and strictly adheres to the package-lock.json file, guaranteeing reproducible builds.

3. Automated Linting and Code Quality

Maintaining consistent code style is vital for team collaboration. We automate this by running the Angular CLI linting command. This step catches syntax errors and style violations before they even reach the testing phase.

    - name: Lint code
      run: npm run lint

This simple step executes the lint script defined in your package.json. If any linting rules are violated, the pipeline will fail immediately, preventing substandard code from progressing further. It is a critical gatekeeper for maintaining the integrity of your codebase, especially when using comprehensive templates like the Angular Dashboard Template.

4. Running Unit Tests in Headless Mode

Testing is the backbone of CI/CD. For Angular, we use Karma and Jasmine, but since the CI runner doesn’t have a graphical user interface, we must run Chrome in “headless” mode. This allows the tests to execute in the background without requiring a display.

    - name: Run unit tests
      run: npm test -- --watch=false --browsers=ChromeHeadlessNoSandbox

The --watch=false flag ensures the test runner executes once and exits, as required by CI. ChromeHeadlessNoSandbox is a custom Karma launcher that adds --no-sandbox to Chrome flags — necessary in most Linux CI runners which lack the privilege isolation required by Chrome’s sandbox. Add it to your karma.conf.js:

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

This step ensures all your business logic remains functional after every change, maintaining the high standards we use in our Free Angular Admin Template.

5. Building for Production

Once the code is linted and tested, it is time to generate the production-ready assets. The Angular CLI optimizes the build by performing ahead-of-time (AOT) compilation, tree-shaking, and minification to ensure the smallest possible bundle size.

    - name: Build production application
      run: npm run build -- --configuration=production

This command triggers the production build. The result is a set of static files (HTML, CSS, JS) located in the dist/ folder. These files are optimized for performance and are what will eventually be served to your users. Passing the --configuration=production flag ensures that environment-specific settings are correctly applied.

6. Security Auditing

Security should never be an afterthought. We include a step to audit our dependencies for known vulnerabilities using npm audit. This helps prevent the introduction of compromised packages into our production environment.

    - name: Security audit
      run: npm audit --audit-level=high

The audit-level=high flag tells the pipeline to only fail if high or critical vulnerabilities are found. This prevents the build from breaking over minor issues while still protecting your application from significant threats. It is a best practice we follow religiously when developing CoreUI components.

7. Deployment to Firebase (Example)

The final stage of CI/CD is deployment. In this example, we deploy to Firebase Hosting. You will need a Firebase Service Account JSON stored as the FIREBASE_SERVICE_ACCOUNT secret in your GitHub repository settings to authenticate the deployment.

    - name: Deploy to Firebase
      uses: FirebaseExtended/action-hosting-deploy@v0
      with:
        repoToken: '${{ secrets.GITHUB_TOKEN }}'
        firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT }}'
        channelId: live
        projectId: your-project-id

This step uses a specialized action to upload your dist/ folder to Firebase. The secrets context allows us to use sensitive credentials without exposing them in the YAML file. Once this step completes, your latest changes are live and accessible to the world.

Best Practice Note:

In CoreUI projects, we always recommend keeping your CI/CD pipelines fast. If your test suite grows too large, consider splitting it into parallel jobs or using a more modern test runner like Vitest for faster execution. Additionally, always ensure your package-lock.json is committed to version control to maintain consistency between your local environment and the CI runner. For production-grade applications, using a pre-configured Angular Dashboard Template can save you weeks of setup time.


Speed up your responsive apps and websites with fully-featured, ready-to-use open-source admin panel templates—free to use and built for efficiency.


About the Author