Skip to content

GitLab CI template for Angular

This project implements a GitLab CI/CD template to build, test and analyse your Angular projects.

Usage

This template can be used both as a CI/CD component or using the legacy include:project syntax.

Use as a CI/CD component

Add the following to your gitlab-ci.yml:

include:
  # 1: include the component
  - component: gitlab.com/to-be-continuous/angular/gitlab-ci-angular@4.8.1
    # 2: set/override component inputs
    inputs:
      cli-image: "registry.hub.docker.com/trion/ng-cli-karma:16.2.9" # ⚠ this is only an example

Use as a CI/CD template (legacy)

Add the following to your gitlab-ci.yml:

include:
  # 1: include the template
  - project: 'to-be-continuous/angular'
    ref: '4.8.1'
    file: '/templates/gitlab-ci-angular.yml'

variables:
  # 2: set/override template variables
  NG_CLI_IMAGE: "registry.hub.docker.com/trion/ng-cli-karma:16.2.9" # ⚠ this is only an example

Global configuration

The Angular template uses some global configuration used throughout all jobs.

Input / Variable Description Default value
cli-image / NG_CLI_IMAGE The Docker image used to run Angular-CLI (ng)
⚠ set the version required by your project
registry.hub.docker.com/trion/ng-cli-karma:latest
npm-config-registry / NPM_CONFIG_REGISTRY NPM registry none (defaults to https://registry.npmjs.org)
npm-config-scoped-registries / NPM_CONFIG_SCOPED_REGISTRIES Space separated list of NPM scoped registries (formatted as @somescope:https://some.npm.registry/some/repo @anotherscope:https://another.npm.registry/another/repo) none
workspace-dir / NG_WORKSPACE_DIR Angular workspace directory .
install-extra-opts / NG_INSTALL_EXTRA_OPTS Extra options to install project dependencies (with npm ci) none

Configuring scoped registries

You may configure scoped registries with the $NPM_CONFIG_SCOPED_REGISTRIES variable. The value is expected as a (whitespace-separated) list of @registry_scope:registry_url.

The Angular template also supports authentication for each, simply by defining the appropriate variable (as project or group secret variables) depending on the desired authentication method:

  • NPM_REGISTRY_<SCOPE>_AUTH_TOKEN: authentication token
  • NPM_REGISTRY_<SCOPE>_AUTH_BASIC: base64 authentication string (base64(username + ':' + password))

⚠ The <SCOPE> part is the registry_scope transformed in SCREAMING_SNAKE_CASE (uppercase words separated by underscores).

Example: declare the GitLab chart repository from another GitLab project

variables:
  NPM_CONFIG_SCOPED_REGISTRIES: "@public-repo:https://public.npm.registry/some/repo @priv-repo:https://private.npm.registry/another/repo"
  # NPM_REGISTRY_PRIV_REPO_AUTH_TOKEN set as a project secret variables

Jobs

ng-lint job

The Angular template features a job ng-lint that performs Angular source code lint.

It is bound to the build stage, and uses the following variable:

Input / Variable Description Default value
lint-args / NG_LINT_ARGS Angular ng lint arguments lint

ng-build job

The Angular template features a job ng-build that performs build and tests all at once.

Those stages are performed in a single job for optimization purpose (it saves time) and also for jobs dependency reasons (some jobs such as SONAR analysis have a dependency on test results).

Those stage are bound to the build stage, and uses the following variable:

Input / Variable Description Default value
test-args / NG_TEST_ARGS Angular ng test arguments test --code-coverage --reporters progress,junit --watch=false --no-progress
build-args / NG_BUILD_ARGS Angular ng build arguments build

The next chapters presents some requirements related to your unit tests (using Karma).

Use a headless browser

To be able to launch unit tests with Angular CLI, the Angular template requires a headless browser within the Docker image NG_CLI_IMAGE (it is the case with the default image, docker-ng-cli-karma).

1. Using Karma

Code Coverage reports

In order to be able to compute and enable GitLab code coverage integration, the Angular template expects the following in your karma.conf.js:

  1. Add the karma-coverage package:
    require('karma-coverage'),
    
  2. Configure the 2 reporters withing this config section:
    coverageReporter: {
      dir: 'reports',
      subdir: '.',
      reporters: [
        // 'text-summary' to let GitLab grab coverage from stdout
        {type: "text-summary"},
        // 'cobertura' to enable GitLab test coverage visualization
        {type: 'cobertura', file: 'ng-coverage.cobertura.xml'}
      ],
    },
    
    ⚠ in case of multiple angular projects in the workspace, each project shall produce its coverage report in reports/ng-coverage-<projectName>.cobertura.xml (it can be in sub-folders but must follow the file name pattern).
  3. Additionally, if using SonarQube, you may also want to generate LCOV report:
    coverageReporter: {
      dir: 'reports',
      subdir: '.',
      reporters: [
        // 'text-summary' to let GitLab grab coverage from stdout
        {type: "text-summary"},
        // 'cobertura' to enable GitLab test coverage visualization
        {type: 'cobertura', file: 'ng-coverage.cobertura.xml'},
        // 'lcovonly' to enable SonarQube test coverage reporting
        {type: 'lcovonly', file: 'ng-coverage.lcov.info'}
      ],
    },
    
Unit Tests reports

In order to be able to integrate your test reports to GitLab:

  1. Add the karma-junit-reporter package as dev dependency:
    npm install --save-dev karma-junit-reporter
    
  2. In your karma.conf.js, add the plugin:
    // 'karma-junit-reporter' to enable GitLab unit test report integration
    require('karma-junit-reporter'),
    
  3. Add the config section:
    // 'karma-junit-reporter' to enable GitLab unit test report integration
    junitReporter: {
      outputDir: 'reports',
      outputFile: 'ng-test.xunit.xml',
      useBrowserName: false,
      ...
    }
    
    ⚠ in case of multiple Angular projects in the workspace, each project shall produce its JUnit report either in reports/ng-test-<projectName>.xunit.xml or reports/<projectName>/ng-test.xunit.xml.

Additionally, if using SonarQube, you may also want to generate SonarQube generic test report:

  1. Add karma-sonarqube-execution-reporter to your project as a dev dependency:
    npm install --save-dev karma-sonarqube-execution-reporter
    
  2. In your karma.conf.js, add the plugin:
    // 'karma-sonarqube-execution-reporter' to enable SonarQube unit test report integration
    require('karma-sonarqube-execution-reporter')
    
  3. Add the config section:
    // 'karma-sonarqube-execution-reporter' to enable SonarQube unit test report integration
    sonarQubeExecutionReporter: {
      outputDir: 'reports',
      outputFile: 'ng-test.sonar.xml',
      ...
    }
    
  4. Finally add the sonarqubeUnit reporter in the reporters parameter of the NG_TEST_ARGS variable :
    NG_TEST_ARGS:  test --reporters junit,sonarqubeUnit`
    

2. Using Jest

Unit Tests reports

To be able to use Jest instead Karma, you first have to install some jest packages. Then you have to create a dedicated jest config file, and to modify your angular.json and tsconfig.spec.json files to set Jest as test builder.

  1. Add jest, jest-junit, jest-preset-angular, @types/jest and @angular-builders/jest to your project as a dev dependency:
    npm install jest jest-preset-angular jest-junit @types/jest @angular-builders/jest --save-dev
    
  2. Create the file jest.config.js, and add the following lines:
    module.exports = {
      reporters: [
        'default',
        ["jest-junit",
        {
          outputDirectory: "reports",
          outputName: "ng-test.xunit.xml"
        }],
      ],
      preset: 'jest-preset-angular',
      globalSetup: 'jest-preset-angular/global-setup',
    };
    
  3. Open the angular.json file. Replace the test builder with jest, and convert "inlineStyleLanguage" option to array instead string:
    "test": {
    // REPLACE: "builder": "@angular-devkit/build-angular:karma",
    // With:
      "builder": "@angular-builders/jest:run",
    ...
    // REPLACE: "inlineStyleLanguage": "scss",
    // With:
      "inlineStyleLanguage": ["scss"],
    
  4. Open the tsconfig.spec.jsonfile and replace the following line:
    "types": [
      // REPLACE: "jasmine"
      // With:
      "jest"
    ]
    
Code Coverage reports
  1. Modify the file jest.config.js, and add the following lines into the module.exports:
    coverageDirectory: "reports",
    coverageReporters: [
      // 'text' to let GitLab grab coverage from stdout
      "text",
      // 'cobertura' to enable GitLab test coverage visualization
      ["cobertura",{file: 'ng-coverage.cobertura.xml'}],
      // [OPTIONAL] only if using SonarQube
      // 'lcovonly' to enable SonarQube test coverage reporting
      "lcovonly",
    ],
    
  2. Open the angular.json file and add the following line to the test options:
    "ci": true,
    "coverage": true,
    
  3. Finally, override the NG_TEST_ARGS from your gitlab-ci.yml variables:
    NG_TEST_ARGS: test --coverage
    

Additionally, if using SonarQube, you may also want to generate SonarQube generic test report:

  1. Add jest-sonar-reporter to your project as a dev dependency:
    npm install --save-dev jest-sonar-reporter
    
  2. In your jest.config.js, add this config line to the exports:
    testResultsProcessor: "jest-sonar-reporter",
    
  3. In your jest.config.js, add a jestSonar section to configure the name of the jest report.
    "devDependencies": {
      ...
    },
    "jestSonar": {
      "reportPath": "reports",
      "reportFile": "ng-test.sonar.xml"
    }
    

ng-e2e job

The Angular template features a job ng-e2e that performs protractor tests This stage is bound to the test stage and uses the following variables :

Input / Variable Description Default value
e2e-args / NG_E2E_ARGS Angular ng e2e arguments e2e
e2e-enabled / NG_E2E_ENABLED set to trueto enable the e2e tests execution none (disabled by default)

Implementation rely on the official Angular CLI tool (ng build and ng test commands).

To enable JUnit reporting on this job, you'll need to add jasmine-reporters dependency to your project and add the following snippet to your protractor config file :

const { JUnitXmlReporter } = require('jasmine-reporters');

exports.config = {
  ...
    onPrepare() {
    jasmine.getEnv().addReporter(new JUnitXmlReporter({
      consolidateAll: true,
      savePath: 'reports',
      filePrefix: 'ng-e2e.xunit'
    }));
  }
  ...
}

ng-sbom job

This job generates a SBOM file listing installed packages using @cyclonedx/cyclonedx-npm.

It is bound to the test stage, and uses the following variables:

Input / Variable Description Default value
sbom-disabled / NG_SBOM_DISABLED Set to true to disable this job none
sbom-version / NG_SBOM_VERSION The version of @cyclonedx/cyclonedx-npm used to emit SBOM none (uses latest)
sbom-opts / NG_SBOM_OPTS Options for @cyclonedx/cyclonedx-npm used for SBOM analysis --omit dev

ng-publish job

This job publishes the project packages to a npm registry.

This job is bound to the publish stage and is disabled by default. When enabled, it is executed on a Git tag with a semantic version pattern (v?[0-9]+\.[0-9]+\.[0-9]+, configurable).

It uses the following variables:

Input / Variable Description Default value
publish-enabled / NG_PUBLISH_ENABLED Set variable to true to enable the publish job none (disabled)
publish-projects / NG_PUBLISH_PROJECTS Space separated list of projects to publish none (all workspace projects are published)
publish-args / NG_PUBLISH_ARGS NPM publish arguments none
NPM_PUBLISH_REGISTRY npm registry to publish to uses GitLab project npm packages registry
🔒 NPM_PUBLISH_TOKEN NPM publication registry authentication token none
🔒 NPM_PUBLISH_AUTH NPM publication registry basic authentication (base64) none

⚠ When using the gitlab registry (which is the default behavior), your NPM package name must be in the format of @scope/package-name:

  • The @scope is the root namespace of the GitLab project. It must match exactly, including the case.
  • The package-name can be whatever you want.

For example, if your project is https://gitlab.example.com/my-org/engineering-group/team-amazing/analytics, the root namespace is my-org. When you publish a package, it must have my-org as the scope. For more details see Package naming convention.

⚠ Don't forget to specify the publication registry in the project(s) to publish package.json file (not the workspace top-level one).

{
  "name": "@my-org/hello-world",
  "version": "0.0.6",
  "peerDependencies": {
    "@angular/common": "^10.1.6",
    "@angular/core": "^10.1.6"
  },
  "dependencies": {
    "tslib": "^2.0.0"
  },
  "publishConfig": {
    "@my-org:registry": "https://<publication-registry-url>"
  }
}

ℹ When using the GitLab registry, the registry publication url looks like https://<gitlab-host>/api/v4/projects/<your_project_id>/packages/npm/, with:

  • <gitlab-host> is your GitLab host domain name.
  • <your_project_id> is your project ID, found on the project’s home page.

SonarQube analysis

If you're using the SonarQube template to analyse your Angular code, here is a sample sonar-project.properties file:

# see: https://docs.sonarqube.org/latest/analyzing-source-code/test-coverage/javascript-typescript-test-coverage/
# set your source directory(ies) here (relative to the sonar-project.properties file)
sonar.sources=app
# exclude unwanted directories and files from being analysed
sonar.exclusions=node_modules/**,dist/**,**/*.spec.ts

# set your tests directory(ies) here (relative to the sonar-project.properties file)
sonar.tests=app
sonar.test.inclusions=**/*.spec.ts

# tests report: generic format
# set the path configured with karma-sonarqube-execution-reporter
sonar.testExecutionReportPaths=reports/ng-test.sonar.xml
# lint report: TSLint JSON
sonar.typescript.tslint.reportPaths=reports/ng-lint.tslint.json
# coverage report: LCOV format
# set the path configured with karma-coverage-istanbul-reporter
sonar.typescript.lcov.reportPaths=reports/ng-coverage.lcov.info

More info: