Commit 34303913 authored by Kiran Patel's avatar Kiran Patel Committed by Pierre Smeyers
Browse files

feat: initial template

parent 5b671fd4
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -9,10 +9,10 @@ include:
    ref: 'master'
    file: '/templates/validation.yml'
  - project: 'to-be-continuous/bash'
    ref: '2.0.0'
    ref: '3.3'
    file: 'templates/gitlab-ci-bash.yml'
  - project: 'to-be-continuous/semantic-release'
    ref: '2.0.2'
    ref: '3.7'
    file: '/templates/gitlab-ci-semrel.yml'    

stages:
+44 −70
Original line number Diff line number Diff line
# GitLab CI template for Spectral

This project implements a GitLab CI/CD template to lint your JSON/YAML with
[Spectral](https://docs.stoplight.io/docs/spectral). Supports OpenAPI and AsyncAPI.
This project implements a GitLab CI/CD template to lint JSON/YAML documents with
[Spectral](https://docs.stoplight.io/docs/spectral) with custom ruleset, and out of the box support for OpenAPI and AsyncAPI.

## Usage

@@ -18,95 +18,69 @@ include:

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

| Name                  | description                            | default value     |
| Name                  | Description                            | Default value     |
| --------------------- | -------------------------------------- | ----------------- |
| `SPECTRAL_IMAGE` | The Docker image used to run `spectral` | `registry.hub.docker.com/spectral:latest` |
| `SPECTRAL_IMAGE` | The Docker image used to run `spectral` | `registry.hub.docker.com/stoplight/spectral:latest` |

## Jobs

### `spectral-build` job
### `spectral` job

This job performs **build and tests** at once.
This job performs a [lint analysis](https://meta.stoplight.io/docs/spectral/docs/guides/2-cli.md) of your API document(s) (OpenAPI, AsyncAPI...), mapped to the `build` stage.

It uses the following variable:

| Name                  | description                              | default value     |
| Name                  | Description                              | Default value     |
| --------------------- | ---------------------------------------- | ----------------- |
| `SPECTRAL_BUILD_ARGS`      | Arguments used by the build job          | `build --with-default-args` |
| `SPECTRAL_DOCUMENTS`  | Location of JSON/YAML documents to be linted. Can be either a file, a glob or fetchable resource(s) on the web          | `{,api/,src/main/resources/}*{openapi,oas,swagger,asyncapi}*.{json,yml,yaml}` <br><br>see [glob syntax](https://github.com/mrmlnc/fast-glob#basic-syntax) for more details |
| `SPECTRAL_EXTRA_ARGS`      | Extra arguments for the [Spectral CLI](https://meta.stoplight.io/docs/spectral/docs/guides/2-cli.md) | _none_ |
| `SPECTRAL_DISABLED`      | Set to `true` to disable this job          | _none_ |

### SonarQube analysis

If you're using the SonarQube template to analyse your SPECTRAL code, here are 2 sample `sonar-project.properties` files.
#### `$SPECTRAL_DOCUMENTS` default value

```properties
# see: https://docs.sonarqube.org/latest/analysis/languages/spectral/
# set your source directory(ies) here (relative to the sonar-project.properties file)
sonar.sources=.
# exclude unwanted directories and files from being analysed
sonar.exclusions=output/**,**/*_test.spectral
The default value is configured to lookup documents as below:

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

# tests report (TODO)
sonar.spectral.testExecutionReportPaths=reports/sonar_test_report.xml
# coverage report (TODO)
sonar.spectral.coverage.reportPaths=reports/coverage.cov
```
# Folder locations 
- ${project_root}
- ${project_root}/api
- ${project_root}/src/main/resources

# Filenames containing below keywords
- openapi
- oas
- swagger
- asyncapi

# File extentions
- json
- yaml
- yml
```
#### Ruleset File

More info:

* [Spectral language support](https://docs.sonarqube.org/latest/analysis/languages/spectral/)
* [test coverage & execution parameters](https://docs.sonarqube.org/latest/analysis/coverage/)
* [third-party issues](https://docs.sonarqube.org/latest/analysis/external-issues/)

### `spectral-lint` job
If you don't specify an explicit ruleset file with the `--ruleset` option, the Spectral CLI looks for a ruleset file called `.spectral.yml`, `.spectral.yaml`, `.spectral.json` or `.spectral.js` in the current working directory.

This job performs a [lint](link-to-the-tool) analysis of your code, mapped to the `build` stage.
If none is present in your project, the template creates a default `.spectral.yaml` file with below content:

It uses the following variables:

| Name                  | description                                | default value     |
| --------------------- | ------------------------------------------ | ----------------- |
| `SPECTRAL_LINT_IMAGE`      | The Docker image used to run the lint tool | `spectral-lint:latest` |
| `SPECTRAL_LINT_DISABLED`   | Set to `true` to disable the `lint` analysis| _none_ (enabled) |
| `SPECTRAL_LINT_ARGS`       | Lint [options and arguments](link-to-the-cli-options) | `--serevity=medium` |
```yaml
extends: spectral:oas           # out-of-the-box OpenAPI ruleset provided by Spectral
```

### `spectral-depcheck` job
#### `$SPECTRAL_EXTRA_ARGS` use cases

This job enables a manual [dependency check](link-to-the-tool) analysis of your code, mapped to the `test` stage.
The `$SPECTRAL_EXTRA_ARGS` can be used to override the defaults for optional arguments. Below are the most probably use cases: 

It uses the following variables:
- Custom Ruleset location

| Name                  | description                                | default value     |
| --------------------- | ------------------------------------------ | ----------------- |
| `SPECTRAL_DEPCHECK_IMAGE`  | The Docker image used to run the dependency check tool | `spectral-depcheck:latest` |
| `SPECTRAL_DEPCHECK_ARGS`   | Dependency check [options and arguments](link-to-the-cli-options) | _none_ |
  Spectral uses the `.spectral.yaml` file in the root folder as convention for the ruleset.
  
### `spectral-publish` job
  If there is a need to specify any other location (remote URL or other filename), the `$SPECTRAL_EXTRA_ARGS` variable shall be overridden.

This job is **disabled by default** and performs a publish of your built binaries.
- Fail-Severity

It uses the following variables:
  Spectral default to `error` fail-severity.
  
| Name                  | description                            | default value     |
| --------------------- | -------------------------------------- | ----------------- |
| `SPECTRAL_PUBLISH_ENABLED` | Variable to enable the publish job     | _none_ (disabled) |
| `SPECTRAL_PUBLISH_ARGS`    | Arguments used by the publish job      | `publish --with-default-args` |
| :lock: `SPECTRAL_PUBLISH_LOGIN` | Login to use to publish           | **has to be defined** |
| :lock: `SPECTRAL_PUBLISH_PASSWORD` | Password to use to publish     | **has to be defined** |

### Secrets management

Here are some advices about your **secrets** (variables marked with a :lock:):

1. Manage them as [project or group CI/CD variables](https://docs.gitlab.com/ee/ci/variables/#create-a-custom-variable-in-the-ui):
    * [**masked**](https://docs.gitlab.com/ee/ci/variables/#mask-a-custom-variable) to prevent them from being inadvertently
      displayed in your job logs,
    * [**protected**](https://docs.gitlab.com/ee/ci/variables/#protect-a-custom-variable) if you want to secure some secrets
      you don't want everyone in the project to have access to (for instance production secrets).
2. In case a secret contains [characters that prevent it from being masked](https://docs.gitlab.com/ee/ci/variables/#masked-variable-requirements), 
  simply define its value as the [Base64](https://en.wikipedia.org/wiki/Base64) encoded value prefixed with `@b64@`:
  it will then be possible to mask it and the template will automatically decode it prior to using it.
3. Don't forget to escape special characters (ex: `$` -> `$$`).
  If there is a need to specify any other location (remote URL or other filename), the `$SPECTRAL_EXTRA_ARGS` variable shall be overridden.
+1 −1
Original line number Diff line number Diff line
@@ -3,7 +3,7 @@ files:
    documentation: ./README.md
    changelog: ./CHANGELOG.md
data:
    description: "JSON/YAML Linter with custom rulesets"
    description: "JSON/YAML Linter with custom rulesets, out of the box support for OpenAPI and AsyncAPI"
    labels:
    - to be continuous
    - Build
+13 −65
Original line number Diff line number Diff line

{
  "name": "Spectral",
  "description": "[Spectral](https://docs.stoplight.io/docs/spectral) is a JSON/YAML Linter with custom rulesets",
  "description": "[Spectral](https://docs.stoplight.io/docs/spectral) is a JSON/YAML Linter with custom rulesets, out of the box support for OpenAPI and AsyncAPI",
  "template_path": "templates/gitlab-ci-spectral.yml",
  "kind": "build",
  "kind": "analyse",
  "variables": [
    {
      "name": "SPECTRAL_IMAGE",
      "description": "The Docker image used to run `spectral`",
      "default": "registry.hub.docker.com/spectral:1.2.3"
      "default": "registry.hub.docker.com/stoplight/spectral:latest"
    },
    {
      "name": "SPECTRAL_BUILD_ARGS",
      "description": "Arguments used by the build job",
      "default": "build --with-default-args",
      "advanced": true
    }
  ],
  "features": [
    {
      "id": "lint",
      "name": "SPECTRAL lint",
      "description": "[SPECTRAL lint](link-to-the-tool) analysis",
      "disable_with": "SPECTRAL_LINT_DISABLED",
      "variables": [
        {
          "name": "SPECTRAL_LINT_IMAGE",
          "description": "The Docker image used to run the lint tool",
          "default": "registry.hub.docker.com/spectral-lint:latest"
        },
        {
          "name": "SPECTRAL_LINT_ARGS",
          "description": "Lint [options and arguments](link-to-the-cli-options)",
          "default": "--serevity=medium",
          "advanced": true
        }
      ]
    },
    {
      "id": "depcheck",
      "name": "SPECTRAL dependency check",
      "description": "[SPECTRAL dependency check](link-to-the-tool) analysis",
      "variables": [
        {
          "name": "SPECTRAL_DEPCHECK_IMAGE",
          "description": "The Docker image used to run the dependency check tool",
          "default": "registry.hub.docker.com/spectral-depcheck:latest"
      "name": "SPECTRAL_DOCUMENTS",
      "description": "Location of JSON/YAML documents to be linted. Can be either a file, a glob or fetchable resource(s) on the web",
      "default": "{,api/,src/main/resources/}*{openapi,oas,swagger,async}*.{json,yml,yaml}"
    },
    {
          "name": "SPECTRAL_DEPCHECK_ARGS",
          "description": "Dependency check [options and arguments](link-to-the-cli-options)",
      "name": "SPECTRAL_EXTRA_ARGS",
      "description": "Extra Spectral CLI [options](https://meta.stoplight.io/docs/spectral/docs/guides/2-cli.md)",
      "advanced": true
        }
      ]
    },
    {
      "id": "publish",
      "name": "Publish",
      "description": "Publish your package to a repository",
      "enable_with": "SPECTRAL_PUBLISH_ENABLED",
      "variables": [
        {
          "name": "SPECTRAL_PUBLISH_ARGS",
          "description": "Arguments used by the publish job",
          "default": "publish --with-default-args",
      "name": "SPECTRAL_DISABLED",
      "description": "Set to `true` to disable this job",
      "type": "boolean",
      "advanced": true
        },
        {
          "name": "SPECTRAL_PUBLISH_LOGIN",
          "description": "Login to use to publish",
          "secret": true
        },
        {
          "name": "SPECTRAL_PUBLISH_PASSWORD",
          "description": "Password to use to publish",
          "secret": true
        }
      ]
    }
  ]
}
+32 −107
Original line number Diff line number Diff line
@@ -55,20 +55,13 @@ workflow:

variables:
  # variabilized tracking image
  TBC_TRACKING_IMAGE: "$CI_REGISTRY/to-be-continuous/tools/tracking:master"
  TBC_TRACKING_IMAGE: "registry.gitlab.com/to-be-continuous/tools/tracking:master"

  # Default Docker image (use a public image - can be overridden)
  SPECTRAL_IMAGE: "registry.hub.docker.com/spectral:latest"
  # Default arguments for 'build' command
  SPECTRAL_BUILD_ARGS: "build --with-default-args"
  SPECTRAL_IMAGE: "registry.hub.docker.com/stoplight/spectral:latest"

  # Default arguments for 'publish' command
  SPECTRAL_PUBLISH_ARGS: "publish --with-default-args"

  SPECTRAL_LINT_IMAGE: "registry.hub.docker.com/spectral-lint:latest"
  SPECTRAL_LINT_ARGS: "--serevity=medium"

  SPECTRAL_DEPCHECK_IMAGE: "registry.hub.docker.com/spectral-depcheck:latest"
  # default file path to look for API specs
  SPECTRAL_DOCUMENTS: "{,api/,src/main/resources/}*{openapi,oas,swagger,async}*.{json,yml,yaml}"

  # default production ref name (pattern)
  PROD_REF: '/^(master|main)$/'
@@ -320,112 +313,44 @@ stages:
    echo "11% covered"
  }

  function maybe_create_default_ruleset() {
    if [[ "echo $SPECTRAL_EXTRA_ARGS" == *"-r "* || "echo $SPECTRAL_EXTRA_ARGS" == *"--ruleset "* ]]; then
      log_info "using the custom ruleset provided in the '-r or --ruleset' option"
    elif [[ -f ".spectral.yaml" || -f ".spectral.yml" || -f ".spectral.json" || -f ".spectral.js" ]]; then
      log_info "\\e[33;1m.spectral.{yaml,yml,json,js}\\e[0m file found for the ruleset"
    else 
      ruleset_file=".spectral.yaml"
      echo "extends: spectral:oas" > $ruleset_file
      log_info "creating the file ${ruleset_file} to be used as default ruleset"
    fi
  }

  unscope_variables
  eval_all_secrets

  # ENDSCRIPT
# job prototype
# defines default Docker image, tracking probe, cache policy and tags
.spectral-base:
  image: $SPECTRAL_IMAGE

spectral:
  image:
    name: $SPECTRAL_IMAGE
    entrypoint: [""]
  services:
  - name: "$TBC_TRACKING_IMAGE"
      command: ["--service", "spectral", "1.0.0"]
  before_script:
    - *spectral-scripts
    - install_ca_certs "${CUSTOM_CA_CERTS:-$DEFAULT_CA_CERTS}"
  # Cache downloaded dependencies and plugins between builds.
  # To keep cache across branches add 'key: "$CI_JOB_NAME"'
  # TODO (if necessary): define cache policy here
  cache:
    # cache shall be per branch per template
    key: "$CI_COMMIT_REF_SLUG-spectral"
    paths:
      - .cache/

# (example) build & test job
spectral-build:
  extends: .spectral-base
  stage: build
  script:
    - mkdir -p -m 777 reports
    # TODO (if possible): $TRACE set enables debug logs on the tool
    # TODO (if possible): force test tool to produce JUnit report(s)
    # TODO (if possible): force test tool to compute code coverage with report
    - spectral ${TRACE+--verbose} --coverage --junit --output=reports/spectral-test.xunit.xml $SPECTRAL_BUILD_ARGS
    - output_coverage
  # TODO: code coverage support and GitLab integration (see: https://docs.gitlab.com/ee/ci/yaml/#coverage)
  coverage: '/^(\d+.\d+\%) covered$/'
  # keep build artifacts and test reports (see: https://docs.gitlab.com/ee/ci/yaml/#artifactsreportsjunit)
  artifacts:
    name: "$CI_JOB_NAME artifacts from $CI_PROJECT_NAME on $CI_COMMIT_REF_SLUG"
    expire_in: 1 day
    reports:
      # TODO: Unit tests use JUnit format and GitLab integration (see: https://docs.gitlab.com/ee/ci/yaml/#artifactsreports)
      junit:
        - reports/spectral-test.xunit.xml
    paths:
        - build/
        - reports/

# (example) linter job
spectral-lint:
  extends: .spectral-base
    command: ["--service", "spectral", "initial" ]
  stage: build
  image: $SPECTRAL_LINT_IMAGE
  # force no dependency
  dependencies: []
  script:
    - mkdir -p -m 777 reports
    - spectral_lint ${TRACE+--verbose} --output=reports/spectral-lint.native.json $SPECTRAL_LINT_ARGS
    - spectral lint --format=pretty --format=junit --output.junit=reports/spectral.xunit.xml $SPECTRAL_EXTRA_ARGS $SPECTRAL_DOCUMENTS
  before_script:
    - !reference [.spectral-scripts]
    - install_ca_certs "${CUSTOM_CA_CERTS:-$DEFAULT_CA_CERTS}"
    - maybe_create_default_ruleset
  artifacts:
    name: "$CI_JOB_NAME artifacts from $CI_PROJECT_NAME on $CI_COMMIT_REF_SLUG"
    expire_in: 1 day
    when: always
    paths:
      - reports/spectral-lint.*
    expire_in: 2 weeks
    reports:
      junit: $CI_PROJECT_DIR/reports/spectral.xunit.xml
  rules:
    # exclude if $SPECTRAL_LINT_DISABLED
    - if: '$SPECTRAL_LINT_DISABLED == "true"'
    - if: $SPECTRAL_DISABLED
      when: never
    # .test-policy rules
    - !reference [.test-policy, rules]

# (example) dependency check job
spectral-depcheck:
  extends: .spectral-base
  stage: test
  image: $SPECTRAL_DEPCHECK_IMAGE
  # force no dependency
  dependencies: []
  script:
    - spectral_depcheck ${TRACE+--verbose} $SPECTRAL_DEPCHECK_ARGS
  rules:
    # on schedule: auto
    - if: '$CI_PIPELINE_SOURCE == "schedule"'
      allow_failure: true
      when: always
    # all other cases: manual & non-blocking
    - when: manual
      allow_failure: true

# (example) publish job activated on env ($SPECTRAL_PUBLISH_ENABLED), with required $SPECTRAL_PUBLISH_LOGIN and $SPECTRAL_PUBLISH_PASSWORD env verification
spectral-publish:
  extends: .spectral-base
  stage: publish
  before_script:
    - *spectral-scripts
    # verify $SPECTRAL_PUBLISH_LOGIN and $SPECTRAL_PUBLISH_PASSWORD are set
    - assert_defined "$SPECTRAL_PUBLISH_LOGIN" 'Missing required env $SPECTRAL_PUBLISH_LOGIN'
    - assert_defined "$SPECTRAL_PUBLISH_PASSWORD" 'Missing required env $SPECTRAL_PUBLISH_PASSWORD'
    - spectral login --login=$SPECTRAL_PUBLISH_LOGIN --password=$SPECTRAL_PUBLISH_PASSWORD
  script:
    - spectral $SPECTRAL_PUBLISH_ARGS
  rules:
    # exclude if $SPECTRAL_PUBLISH_ENABLED unset
    - if: '$SPECTRAL_PUBLISH_ENABLED != "true"'
      when: never
    # on integration or production branch(es): manual & non-blocking
    - if: '$CI_COMMIT_REF_NAME =~ $INTEG_REF || $CI_COMMIT_REF_NAME =~ $PROD_REF'
      when: manual
      allow_failure: true