Newer
Older
This project implements a GitLab CI/CD template to build, test and analyse your
[Angular](https://angular.io/) projects.
## Usage
In order to include this template in your project, add the following to your `gitlab-ci.yml`:
```yaml
include:
file: '/templates/gitlab-ci-angular.yml'
```
## Global configuration
The Angular template uses some global configuration used throughout all jobs.
| Name | description | default value |
|----------------|-----------------------------------------------|:--------------------------------------------------------------------------|
| `NG_CLI_IMAGE` | The Docker image used to run Angular-CLI (ng) <br/>:warning: **set the version required by your project** | `registry.hub.docker.com/trion/ng-cli-karma:latest` |
| `NPM_CONFIG_REGISTRY` | NPM [registry](https://docs.npmjs.com/configuring-your-registry-settings-as-an-npm-enterprise-user) | _none_ (defaults to `https://registry.npmjs.org`) |
| `NPM_CONFIG_SCOPED_REGISTRIES` | Space separated list of NPM [scoped registries](https://docs.npmjs.com/cli/v8/using-npm/scope#associating-a-scope-with-a-registry) (formatted as `@somescope:https://some.npm.registry/some/repo @anotherscope:https://another.npm.registry/another/repo`) | _none_ |
| `NG_WORKSPACE_DIR` | Angular workspace directory | `.` |
| `NG_INSTALL_EXTRA_OPTS`| Extra options to install project dependencies (with [`npm ci`](https://docs.npmjs.com/cli/ci.html/)) | _none_ |
### Configuring scoped registries
You may configure [scoped registries](https://docs.npmjs.com/cli/v8/using-npm/scope#associating-a-scope-with-a-registry) 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 tokens for each, simply by defining `NPM_REGISTRY_<SCOPE>_AUTH` (as project or group secret variables).
:warning: The `<SCOPE>` part is the `registry_scope` transformed in [SCREAMING_SNAKE_CASE](https://en.wikipedia.org/wiki/Snake_case) (uppercase words separated by underscores).
Example: declare the GitLab chart repository from another GitLab project
```yml
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 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 `check` stage, and uses the following variable:
| Name | description | default value |
|----------------|----------------------------------------------------------|---------------|
| `NG_LINT_ARGS` | Angular [ng lint](https://angular.io/cli/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:
| Name | description | default value |
|-----------------|------------------------------------------------------------|---------------------------------------------------|
| `NG_TEST_ARGS` | Angular [ng test](https://angular.io/cli/test) arguments | `test --code-coverage --reporters progress,junit` |
| `NG_BUILD_ARGS` | Angular [ng build](https://angular.io/cli/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](https://github.com/trion-development/docker-ng-cli-karma)).
#### 1. Using Karma
##### Code Coverage reports
In order to be able to compute and enable [GitLab code coverage integration](https://docs.gitlab.com/ee/user/project/merge_requests/test_coverage_visualization.html),
the Angular template expects the following in your `karma.conf.js`:
1. Add the [karma-coverage](https://www.npmjs.com/package/karma-coverage) package:
```js
require('karma-coverage'),
```
2. Configure the 2 reporters withing this config section:
```js
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'}
],
},
```
:warning: 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](https://docs.sonarqube.org/latest/analysis/test-coverage/javascript-typescript-test-coverage/):
```js
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'}
],
},
```
In order to be able to [integrate your test reports to GitLab](https://docs.gitlab.com/ee/ci/yaml/artifacts_reports.html#artifactsreportsjunit):
1. Add the [karma-junit-reporter](https://github.com/karma-runner/karma-junit-reporter) package as dev dependency:
```shell
npm install --save-dev karma-junit-reporter
```
2. In your `karma.conf.js`, add the plugin:
```js
// 'karma-junit-reporter' to enable GitLab unit test report integration
require('karma-junit-reporter'),
```
3. Add the config section:
```js
// 'karma-junit-reporter' to enable GitLab unit test report integration
junitReporter: {
outputDir: 'reports',
outputFile: 'ng-test.xunit.xml',
useBrowserName: false,
...
}
```
:warning: 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](https://docs.sonarqube.org/latest/analysis/generic-test/):
1. Add [karma-sonarqube-execution-reporter](https://github.com/lisrec/karma-sonarqube-execution-reporter) to your project as a dev dependency:
```shell
npm install --save-dev karma-sonarqube-execution-reporter
```
2. In your `karma.conf.js`, add the plugin:
```js
// 'karma-sonarqube-execution-reporter' to enable SonarQube unit test report integration
require('karma-sonarqube-execution-reporter')
```
3. Add the config section:
```js
// '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 :
```yaml
NG_TEST_ARGS: test --reporters junit,sonarqubeUnit`
```
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
#### 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](https://www.npmjs.com/package/jest), [jest-junit](https://github.com/jest-community/jest-junit#readme), [jest-preset-angular](https://www.npmjs.com/package/jest-preset-angular), [@types/jest](https://www.npmjs.com/package/@types/jest) and [@angular-builders/jest](https://www.npmjs.com/package/@angular-builders/jest) to your project as a dev dependency:
```shell
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:
```js
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:
```js
"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.json`file and replace the following line:
```js
"types": [
// REPLACE: "jasmine"
// With:
"jest"
]
```
##### Code Coverage reports
1. Modify the file `jest.config.js`, and add the following lines into the module.exports:
```js
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:
```js
"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](https://docs.sonarqube.org/latest/analysis/generic-test/):
1. Add [jest-sonar-reporter](https://www.npmjs.com/package/jest-sonar-reporter) to your project as a dev dependency:
```shell
npm install --save-dev jest-sonar-reporter
```
2. In your `jest.config.js`, add this config line to the exports:
```js
testResultsProcessor: "jest-sonar-reporter",
```
3. In your `jest.config.js`, add a jestSonar section to configure the name of the jest report.
```js
"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 :
| Name | description | default value |
|----------------------|------------------------------------------------------------|------------------------------------------|
| `NG_E2E_ARGS` | Angular [ng e2e](https://angular.io/cli/e2e) arguments | `e2e` |
| `NG_E2E_ENABLED` | set to `true`to enable the e2e tests execution | *none (disabled by default)* |
Implementation rely on the official [Angular CLI](https://cli.angular.io/) tool (`ng build` and `ng test` commands).
To enable JUnit reporting on this job, you'll need to add [jasmine-reporters](https://www.npmjs.com/package/jasmine-reporters) dependency to your project and add the following snippet to your protractor config file :
```js
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](https://cyclonedx.org/) file listing installed packages using [@cyclonedx/cyclonedx-npm](https://www.npmjs.com/package/@cyclonedx/cyclonedx-npm).
It is bound to the `test` stage, and uses the following variables:
| Name | description | default value |
| --------------------- | -------------------------------------- | ----------------- |
| `NG_SBOM_DISABLED` | Set to `true` to disable this job | _none_ |
| `NG_SBOM_VERSION` | The version of @cyclonedx/cyclonedx-npm used to emit SBOM | _none_ (uses latest) |
| `NG_SBOM_OPTS` | Options for @cyclonedx/cyclonedx-npm used for SBOM analysis | `--omit dev` |
### `ng-publish` job
The Angular template features a `ng-publish` job to publish the built project.
This job is bound to the `publish` stage, and uses the following variable:
| Name | description | default value |
|----------------------------|-----------------------------------------------------------------------------|------------------------------------------------------------------|
| `NG_PUBLISH_ENABLED` | Set variable to `true` to enable the publish job | none (disabled) |
| `NG_PUBLISH_PROJECTS` | Space separated list of projects to publish | If no project is specified, all workspace projects are published |
| `NG_PUBLISH_ARGS` | NPM [publish](https://docs.npmjs.com/cli/v6/commands/npm-publish) arguments | `--verbose` |
| `NPM_PUBLISH_REGISTRY` | NPM registry to publish to | uses GitLab project npm packages registry | |
| :lock: `NPM_PUBLISH_TOKEN` | NPM publication registry authentication token | none |
:warning: 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](https://docs.gitlab.com/ee/user/packages/npm_registry/#package-naming-convention).
:warning: Don't forget to specify the publication registry in the **project(s)** to publish `package.json` file (not the workspace top-level one).
```json
{
"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>"
}
}
```
:information_source: When using the GitLab registry, the registry publication url looks like `https://<gitlab-host>/api/v4/projects/<your_project_id>/packages/npm/`, with:
* `<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:
```properties
# 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
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
* [TypeScript language support](https://docs.sonarqube.org/latest/analyzing-source-code/test-coverage/javascript-typescript-test-coverage/)
* [test coverage & execution parameters](https://docs.sonarqube.org/latest/analysis/coverage/)
* [third-party issues](https://docs.sonarqube.org/latest/analysis/external-issues/)