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: $CI_SERVER_FQDN/to-be-continuous/angular/gitlab-ci-angular@4.10.2
# 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.10.2"
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 tokenNPM_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).
The following chapters detail the required configuration (depending on the unit testing framework you're using) in order to integrate your unit tests reports and code coverage reports to GitLab.
Additionally, if also using SonarQube, you'll have to enable some extra reporters.
Unit testing with Karma¶
At least up to Angular 16, Karma was the default unit testing framework.
Here is the required configuration if you're using Karma as unit testing framework.
Reporter | Needs npm install |
Expected report file | Usage |
---|---|---|---|
karma-junit-reporter | Yes | reports/ng-test.xunit.xml |
GitLab unit tests integration (JUnit format) |
karma-coverage text-summary reporter | No | N/A (stdout) | GitLab MR test coverage results (GitLab grabs coverage from stdout) |
karma-coverage cobertura reporter | No | reports/ng-coverage.cobertura.xml |
GitLab code coverage integration (Cobertura format) |
karma-sonarqube-execution-reporter | Yes | reports/ng-test.sonar.xml |
SonarQube unit tests integration (generic SonarQube format) |
karma-coverage lcovonly reporter | No | reports/ng-coverage.lcov.info |
SonarQube code coverage integration (JS/TS LCOV format) |
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
:
- Add the karma-coverage package:
require('karma-coverage'),
- 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).
- 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:
- Add the karma-junit-reporter package as dev dependency:
npm install --save-dev karma-junit-reporter
- In your
karma.conf.js
, add the plugin:
// 'karma-junit-reporter' to enable GitLab unit test report integration
require('karma-junit-reporter'),
- 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:
- Add karma-sonarqube-execution-reporter to your project as a dev dependency:
npm install --save-dev karma-sonarqube-execution-reporter
- In your
karma.conf.js
, add the plugin:
// 'karma-sonarqube-execution-reporter' to enable SonarQube unit test report integration
require("karma-sonarqube-execution-reporter");
- Add the config section:
// 'karma-sonarqube-execution-reporter' to enable SonarQube unit test report integration
sonarQubeExecutionReporter: {
outputDir: 'reports',
outputFile: 'ng-test.sonar.xml',
...
}
- Finally add the
sonarqubeUnit
reporter in the reporters parameter of theNG_TEST_ARGS
variable :
NG_TEST_ARGS: test --reporters junit,sonarqubeUnit`
Unit testing with Jest¶
be aware that Jest is not the default unit testing framework with Angular (up to Angular 16, Karma is the default unit testing framework). The rest of the documentation assumes you've properly uninstalled Karma, and replaced it with Jest. Please refer to the appropriate documentation.
Here is the required configuration if you're using Jest as unit testing framework.
Reporter | Needs npm install |
Expected report file | Usage |
---|---|---|---|
jest-junit | Yes | reports/ng-test.xunit.xml |
GitLab unit tests integration (JUnit format) |
istanbul text | No | N/A (stdout) | GitLab MR test coverage results (GitLab grabs coverage from stdout) |
istanbul cobertura | No | reports/ng-coverage.cobertura.xml |
GitLab code coverage integration (Cobertura format) |
jest-sonar | Yes | reports/ng-test.sonar.xml |
SonarQube unit tests integration (generic SonarQube format) |
istanbul lcovonly | No | reports/ng-coverage.lcov.info |
SonarQube code coverage integration (JS/TS LCOV format) |
The following packages will have to be installed to your project as a dev dependencies: jest, jest-junit, jest-preset-angular, @types/jest and @angular-builders/jest
npm install jest jest-preset-angular jest-junit @types/jest @angular-builders/jest --save-dev
Then edit your jest.config.js
configuration file with all the above reporters configured as expected:
module.exports = {
preset: "jest-preset-angular",
globalSetup: "jest-preset-angular/global-setup",
reporters: [
"default",
// 'jest-junit' to enable GitLab unit test report integration
[
"jest-junit",
{
outputDirectory: "reports",
outputName: "ng-test.xunit.xml",
},
],
// [OPTIONAL] only if using SonarQube
// 'jest-sonar' to enable SonarQube unit test report integration
[
"jest-sonar",
{
outputDirectory: "reports",
outputName: "ng-test.sonar.xml",
},
],
],
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", { file: "ng-coverage.lcov.info" }],
],
};
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 true to 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-audit
job¶
This job performs an audit using (npm audit), to find security vulnerabilities.
It is bound to the test
stage.
Input / Variable | Description | Default value |
---|---|---|
audit-disabled / NG_AUDIT_DISABLED |
Set to true to disable npm audit |
none (enabled) |
audit-args / NG_AUDIT_ARGS |
npm audit arguments | --audit-level=low |
In addition to a textual report in the console, this job produces the following report, kept for one day:
Report | Format | Usage |
---|---|---|
$NG_WORKSPACE_DIR/reports/ng-audit.native.json |
JSON | DefectDojo integration This report is generated only if DefectDojo template is detected, if needed, you can force it with $DEFECTDOJO_NPMAUDIT_REPORTS |
ng-outdated
job¶
This job performs outdated analysis (npm outdated), to find dependencies that might be updated.
It is bound to the test
stage.
Input / Variable | Description | Default value |
---|---|---|
outdated-disabled / NG_OUTDATED_DISABLED |
Set to true to disable npm outdated job |
none (enabled) |
outdated-args / NG_OUTDATED_ARGS |
npm outdated arguments | --long |
outdated-allow-failure / NG_OUTDATED_ALLOW_FAILURE |
Allow the job to fail and therefore not block the pipeline | true |
The job generates an outdated report that you will find here: NG_WORKSPACE_DIR/reports/ng-outdated.txt
.
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 | none (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
sonar.testExecutionReportPaths=reports/ng-test.sonar.xml
# lint report: TSLint JSON
sonar.typescript.tslint.reportPaths=reports/ng-lint.tslint.json
# coverage report: LCOV format
sonar.typescript.lcov.reportPaths=reports/ng-coverage.lcov.info
More info: