Code Quality

使用 Code Quality 来分析源代码的质量和复杂性,有助于使您的项目代码简单、可读且易于维护。Code Quality 应该补充您的其他评审流程,而不是取代它们。

Code Quality 使用开源 Code Climate 工具,并选择插件来分析您的源代码。 要确认您的代码的语言是否包含在内,请参阅支持的可维护性语言的 Code Climate 列表。 您可以使用 Code Climate 分析插件自定义工具来扩展代码覆盖率。

在 CI/CD 流水线中运行 Code Quality 报告,验证变更不会降低代码质量,然后再将它们提交到默认分支。

产品级别功能摘要

不同的极狐GitLab 产品级别提供不同的功能,如下表所示:

能力 基础版 专业版 旗舰版
配置扫描器
集成自定义扫描器
在合并请求部件中查看结果
生成 JSON 或 HTML 报告产物
查看 CI 流水线中的报告
在合并请求差异视图中查看结果
在项目质量摘要视图上分析全局健康 No No Yes

查看 Code Quality 结果

Code Quality 结果显示在:

  • 合并请求部件
  • 合并请求变更视图
  • 流水线详情视图
  • 项目质量视图

合并请求部件

如果来自目标分支的报告可用于比较,Code Quality 分析结果将显示在合并请求部件区域中。

Code Quality Widget

查看 Code Climate 支持的可维护性语言列表。

合并请求变更视图

Code Quality 结果显示在合并请求变更视图中。对于包含 Code Quality 问题的行,会在装订线旁边显示标记。将鼠标悬停在标记上可以了解问题的详细信息。

Code Quality MR diff report

流水线详情视图

流水线生成的 Code Quality 违规的完整列表显示在流水线详细信息页面的代码质量选项卡中。

Code Quality Report

项目质量视图

  • 引入于极狐GitLab 14.5,使用名为 project_quality_summary_page特性标志。默认禁用。

项目质量视图显示代码质量发现的概述。该视图可在分析 > CI/CD 分析下找到,需要为特定项目启用 project_quality_summary_page 功能标志。

Code Quality Summary

启用 Code Quality

先决条件:

  • 极狐GitLab CI/CD 配置(.gitlab-ci.yml)必须包括 test 阶段。
  • 如果您使用的是共享 runner,则必须为 Docker-in-Docker 工作流 配置 Code Quality 作业。
  • 如果您使用私有 runner,您应该使用推荐的替代配置,更有效地运行 Code Quality 分析。
  • Runner 必须有足够的磁盘空间来存储生成的 Code Quality 文件。例如,在极狐GitLab 项目上,文件大约为 7 GB。

要启用代码质量,可以:

  • 启用 Auto DevOps,它包含自动代码质量

    示例:

     include:
     - template: Jobs/Code-Quality.gitlab-ci.yml
    

    Code Quality 现在在流水线中运行。

caution 在私有化部署版实例上,如果恶意行为者破坏 Code Quality 作业定义,他们可以在 runner 主机上执行特权 Docker 命令。拥有适当的访问控制策略,通过只允许受信任的参与者访问来减缓这种攻击向量。

使用私有 runners 提升 Code Quality 性能

如果您有私有 runner,您应该使用以下配置来提高 Code Quality 的性能:

  • 不使用特权模式。
  • 不使用 Docker-in-Docker。
  • Docker 镜像,包括所有 CodeClimate 镜像都被缓存,并且不会为后续作业重新获取。

此替代配置使用套接字绑定与作业环境共享 Runner 的 Docker 守护进程。在实施此配置之前,请考虑其限制

使用私有 runners:

  1. 注册一个新的 runner:

    $ gitlab-runner register --executor "docker" \
      --docker-image="docker:stable" \
      --url "https://jihulab.com/" \
      --description "cq-sans-dind" \
      --tag-list "cq-sans-dind" \
      --locked="false" \
      --access-level="not_protected" \
      --docker-volumes "/cache"\
      --docker-volumes "/builds:/builds"\
      --docker-volumes "/var/run/docker.sock:/var/run/docker.sock" \
      --registration-token="<project_token>" \
      --non-interactive
    
  2. 可选,但推荐:将构建目录设置为 /tmp/builds,以便定期从 runner 主机中清除作业产物。如果跳过这一步,您必须自己清理默认的构建目录(/builds)。您可以通过在上一步中,将以下两个标志添加到 gitlab-runner register 来做到这一点。

    --builds-dir "/tmp/builds"
    --docker-volumes "/tmp/builds:/tmp/builds" # Use this instead of --docker-volumes "/builds:/builds"
    

    生成的配置:

    [[runners]]
      name = "cq-sans-dind"
      url = "https://jihulab.com/"
      token = "<project_token>"
      executor = "docker"
      builds_dir = "/tmp/builds"
      [runners.docker]
        tls_verify = false
        image = "docker:stable"
        privileged = false
        disable_entrypoint_overwrite = false
        oom_kill_disable = false
        disable_cache = false
        volumes = ["/cache", "/var/run/docker.sock:/var/run/docker.sock", "/tmp/builds:/tmp/builds"]
        shm_size = 0
      [runners.cache]
        [runners.cache.s3]
        [runners.cache.gcs]
    
  3. 对模板创建的 code_quality 作业应用两个覆盖:

    include:
      - template: Code-Quality.gitlab-ci.yml
    
    code_quality:
      services:            # Shut off Docker-in-Docker
      tags:
        - cq-sans-dind     # Set this job to only run on our new specialized runner
    

Code Quality 现在在标准 Docker 模式下运行。

使用私有 runner 运行无 root 的代码质量

If you are using private runners and would like to run the Code Quality scans in rootless Docker mode code quality requires some special changes to allow it to run properly. This may require having a runner dedicated to running only code quality jobs because changes in socket binding may cause problems in other jobs.

如果您正在使用私有 runner 并且希望在无 root 的 Docker 模式下运行代码质量扫描,则代码质量需要一些特殊更改,以允许它正确运行。这可能需要有一个专用的 runner 仅用于运行代码质量作业,因为更改套接字绑定可能会导致其他作业出现问题。

要使用无 root 私有 runner:

  1. 注册一个新的 runner:

    Replace /run/user//docker.sock with the path to the local docker.sock for the gitlab-runner user. 为 `gitlab-runner` 用户的 `docker.sock` 的路径替换 `/run/user//docker.sock`。

    --builds-dir "/tmp/builds"
    --docker-volumes "/tmp/builds:/tmp/builds" # Use this instead of --docker-volumes "/builds:/builds"
    

    生成的配置:

       $ gitlab-runner register --executor "docker" \
         --docker-image="docker:latest" \
         --url "https://gitlab.com/" \
         --description "cq-rootless" \
         --tag-list "cq-rootless" \
         --locked="false" \
         --access-level="not_protected" \
         --docker-volumes "/cache" \
         --docker-volumes "/tmp/builds:/tmp/builds" \
         --docker-volumes "/run/user/<gitlab-runner-user>/docker.sock:/run/user/<gitlab-runner-user>/docker.sock" \
         --token "<project_token>" \
         --non-interactive \
         --builds-dir "/tmp/builds" \
         --env "DOCKER_HOST=unix:///run/user/<gitlab-runner-user>/docker.sock" \
         --docker-host "unix:///run/user/<gitlab-runner-user>/docker.sock"
    

    配置结果为:

       [[runners]]
         name = "cq-rootless"
         url = "https://gitlab.com/"
         token = "<project_token>"
         executor = "docker"
         builds_dir = "/tmp/builds"
         environment = ["DOCKER_HOST=unix:///run/user/<gitlab-runner-user>/docker.sock"]
         [runners.docker]
           tls_verify = false
           image = "docker:latest"
           privileged = false
           disable_entrypoint_overwrite = false
           oom_kill_disable = false
           disable_cache = false
           volumes = ["/cache", "/run/user/<gitlab-runner-user>/docker.sock:/run/user/<gitlab-runner-user>/docker.sock", "/tmp/builds:/tmp/builds"]
           shm_size = 0
           host = "unix:///run/user/<gitlab-runner-user>/docker.sock"
         [runners.cache]
           [runners.cache.s3]
           [runners.cache.gcs]
    
  2. 应用如下内容来覆盖由模版创建的 code_quality 作业:

     code_quality:
       services:
       variables:
         DOCKER_SOCKET_PATH: /run/user/997/docker.sock
       tags:
         - cq-rootless
    

禁用 Code Quality

如果存在 $CODE_QUALITY_DISABLED CI/CD 变量,则 code_quality 作业不会运行。有关如何定义变量的更多信息,请参阅极狐GitLab CI/CD 变量

要禁用 Code Quality,请创建一个名为 CODE_QUALITY_DISABLED 的自定义 CI/CD 变量,用于:

自定义扫描设置

您可以使用 .gitlab-ci.yml 中的 CI/CD 变量,更改 Code Quality 扫描设置。

配置 Code Quality 作业:

  1. 在包含模板后,声明一个与 Code Quality 作业同名的作业。
  2. 在作业中指定附加键。

有关示例,请参阅下载 JSON 格式的输出

可用的 CI/CD 变量

您可以通过定义可用的 CI/CD 变量来自定义 Code Quality:

CI/CD 变量 描述
CODECLIMATE_DEBUG 设置启用 Code Climate debug 模式
CODECLIMATE_DEV 设置为启用 --dev 模式,该模式允许您运行 CLI 未知的引擎。
CODECLIMATE_PREFIX 设置前缀以用于 CodeClimate 引擎中的所有 docker pull 命令,适用于离线扫描
CODECLIMATE_REGISTRY_USERNAME 设置为从 CODECLIMATE_PREFIX 解析的镜像仓库域名的指定用户名。
CODECLIMATE_REGISTRY_PASSWORD 设置为从 CODECLIMATE_PREFIX 解析的镜像仓库域名的指定密码。
CODE_QUALITY_DISABLED 阻止 Code Quality 作业运行。
CODE_QUALITY_IMAGE 设置为完成的前缀镜像名称。镜像必需能从您的作业环境中被访问。
ENGINE_MEMORY_LIMIT_BYTES 设置引擎的内存限制,默认为 1,024,000,000 字节。
REPORT_STDOUT 设置将报告打印到 STDOUT,而不是生成通常的报告文件。
REPORT_FORMAT 设置控制生成的报告文件的格式。例如:json\|html
SOURCE_CODE 要扫描的源代码的路径。
TIMEOUT_SECONDS codeclimate analyze 命令的自定义超时。

输出

Code Quality 会创建一个名为 gl-code-quality-report.json 的文件。此文件的内容在内部处理,结果显示在 UI 中。要查看原始结果,您可以配置 Code Quality 作业允许下载此文件。格式选项是 JSON 格式、HTML 格式或两者都有。使用 HTML 格式以更易于阅读的格式查看报告。例如,您可以在 Pages 上发布 HTML 格式文件,以便更轻松地查看。

以 HTML 和 JSON 格式输出报告

要以 JSON 和 HTML 格式输出代码质量报告,您需要创建一个额外的作业。这需要运行两次代码质量,每次一个文件格式。

要以 HTML 格式输出代码质量报告,请使用 extends: code_quality 向模板添加另一个作业:

include:
  - template: Jobs/Code-Quality.gitlab-ci.yml

code_quality_html:
  extends: code_quality
  variables:
    REPORT_FORMAT: html
  artifacts:
    paths: [gl-code-quality-report.html]

JSON 和 HTML 文件都会被作为作业产物输出。HTML 文件包含在 artifacts.zip 作业产物中。

仅输出 HTML 格式

下载 HTML 格式的代码质量报告,将 REPORT_FORMAT 设置为 html,覆盖 code_quality 作业的默认定义。

note 这不会创建一个 JSON 格式文件,因此代码质量结果不会在合并请求小部件、流水线报告或更改视图中显示。
include:
  - template: Jobs/Code-Quality.gitlab-ci.yml

code_quality:
  variables:
    REPORT_FORMAT: html
  artifacts:
    paths: [gl-code-quality-report.html]

HTML 文件会输出为作业产物。

将 Code Quality 与合并请求流水线一起使用

默认 Code Quality 配置不允许 code_quality 作业在合并请求流水线上运行。

要使 Code Quality 能够在合并请求流水线上运行,请覆盖代码质量 rulesworkflow: rules,以便它们与您当前的rules 相匹配。

例如:

include:
  - template: Code-Quality.gitlab-ci.yml

code_quality:
  rules:
    - if: $CODE_QUALITY_DISABLED
      when: never
    - if: $CI_PIPELINE_SOURCE == "merge_request_event" # Run code quality job in merge request pipelines
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH      # Run code quality job in pipelines on the default branch (but not in other branch pipelines)
    - if: $CI_COMMIT_TAG                               # Run code quality job in pipelines for tags

使用私有容器镜像库

使用私有容器镜像仓库可以减少下载镜像的时间,也可以减少外部依赖。由于容器执行的嵌套架构,镜像库前缀必须专门配置为向下传递到 CodeClimate 的后续单个 engine 的 docker pull 命令中。

以下变量可以解决所有必需的镜像拉取:

  • CODE_QUALITY_IMAGE:一个完全前缀的镜像名称,可以位于从您的工作环境访问的任何位置。GitLab Container Registry 可用于托管您自己的副本。
  • CODECLIMATE_PREFIX:您预期的容器镜像库的域名,这是 CodeClimate CLI 支持的配置选项。您必须:
    • 包括尾部斜杠 (/)。
    • 不包括协议前缀,例如 https://
  • CODECLIMATE_REGISTRY_USERNAME:一个可选变量,用于指定从 CODECLIMATE_PREFIX 解析的镜像库域名的用户名。
  • CODECLIMATE_REGISTRY_PASSWORD:一个可选变量,用于指定从 CODECLIMATE_PREFIX 解析的镜像库域名的密码。
include:
  - template: Code-Quality.gitlab-ci.yml

code_quality:
  variables:
    CODE_QUALITY_IMAGE: "my-private-registry.local:12345/codequality:0.85.24"
    CODECLIMATE_PREFIX: "my-private-registry.local:12345/"

此示例特定于 Code Quality。有关如何使用镜像库镜像配置 DinD 的更多一般说明,请参阅相关文档

必需的镜像

默认的 .codeclimate.yml 需要以下镜像:

  • codeclimate/codeclimate-structure:latest
  • codeclimate/codeclimate-csslint:latest
  • codeclimate/codeclimate-coffeelint:latest
  • codeclimate/codeclimate-duplication:latest
  • codeclimate/codeclimate-eslint:latest
  • codeclimate/codeclimate-fixme:latest
  • codeclimate/codeclimate-rubocop:rubocop-0-92

如果您使用自定义的 .codeclimate.yml 配置文件,则必须在私有容器镜像库中添加指定的插件。

使用经身份验证的 DockerHub

您可以使用 DockerHub 作为 Code Quality 镜像的替代来源。

先决条件:

要使用 DockerHub,请在 .gitlab-ci.yml 文件中配置以下变量:

  • CODECLIMATE_PREFIX
  • CODECLIMATE_REGISTRY_USERNAME
  • CODECLIMATE_REGISTRY_PASSWORD

示例:

include:
  - template: Jobs/Code-Quality.gitlab-ci.yml

code_quality:
  variables:
    CODECLIMATE_PREFIX: "registry-1.docker.io/"
    CODECLIMATE_REGISTRY_USERNAME: $DOCKERHUB_USERNAME
    CODECLIMATE_REGISTRY_PASSWORD: $DOCKERHUB_PASSWORD

使用 Dependency Proxy

您可以使用 Dependency Proxy 来减少下载依赖项所花费的时间。

先决条件:

要引用 Dependency Proxy,请在 .gitlab-ci.yml 文件中配置以下变量:

  • CODE_QUALITY_IMAGE
  • CODECLIMATE_PREFIX
  • CODECLIMATE_REGISTRY_USERNAME
  • CODECLIMATE_REGISTRY_PASSWORD

例如:

include:
  - template: Code-Quality.gitlab-ci.yml

code_quality:
  variables:
    ## You must add a trailing slash to `$CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX`.
    CODECLIMATE_PREFIX: $CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX/
    CODECLIMATE_REGISTRY_USERNAME: $CI_DEPENDENCY_PROXY_USER
    CODECLIMATE_REGISTRY_PASSWORD: $CI_DEPENDENCY_PROXY_PASSWORD

使用自定义工具

您可以将自定义工具集成到极狐GitLab 中,提供 Code Quality 报告。

Code Quality 报告产物 JSON 文件必须包含具有以下属性的对象数组:

名称 描述
description 代码质量违规的描述。
check_name 表示发出此问题的静态分析检查的唯一名称。
fingerprint 用于识别代码质量违规的唯一指纹。例如,MD5 哈希。
severity 严重性字符串(可以是 infominormajorcriticalblocker)。
location.path 包含代码质量违规的文件的相对路径。
location.lines.beginlocation.positions.begin.line 发生代码质量违规的行。
note 尽管 Code Climate 规范支持更多属性,但极狐GitLab 会忽略这些属性。极狐GitLab 解析器不允许在文件开头使用字节顺序标记。

要使用自定义 Code Quality 工具:

  1. 在生成 Code Quality 报告产物.gitlab-ci.yml 文件中定义一个作业。
  2. 配置工具,将 Code Quality 报告产物生成为 JSON 文件,该文件实现了 Code Climate 规范

示例:

[
  {
    "description": "'unused' is assigned a value but never used.",
    "check_name": "no-unused-vars",
    "fingerprint": "7815696ecbf1c96e6894b779456d330e",
    "severity": "minor",
    "location": {
      "path": "lib/index.js",
      "lines": {
        "begin": 42
      }
    }
  }
]

集成多个工具

代码质量会讲流水线中所有作业的结果整合到单个的 gl-code-quality-report.json 文件中。结果就是,可以在流水线中使用多个独立的工具,无论是同时使用,还是代替支持的 Code-Quality.gitlab-ci.yml 模板。

下面的示例返回了所需的格式的 ESLint 输出:

eslint:
  image: node:18-alpine
  script:
    - npm ci
    - npx eslint --format gitlab .
  artifacts:
    reports:
      codequality: gl-code-quality-report.json

使用分析插件

可以使用 Code Climate 分析插件 扩展 Code Quality 功能。

例如,要使用 SonarJava 分析器

  1. 将名为 .codeclimate.yml 的文件添加到仓库的根目录。
  2. 将插件的 enablement code 添加到仓库根目录的 .codeclimate.yml 中:
  version: "2"
  plugins:
    sonar-java:
      enabled: true

这样会将 SonarJava 添加到包含在您的项目中的默认 .codeclimate.ymlplugins: 部分。

plugins: 部分的更改不会影响默认 .codeclimate.ymlexclude_patterns 部分。有关详细信息,请参阅[排除文件和文件夹] (https://docs.codeclimate.com/docs/exclude-files-and-folders)的 Code Climate 文档。

在 Kubernetes 和 OpenShift 中使用代码质量

您必须在 Docker-in-Docker 环境中使用代码质量。Kubernetes 执行器 支持 Docker-in-Docker

要确保代码质量作业,可以在 Kubernetes 执行器上运行:

  • 如果您正在使用 TLS 来和 Docker 守护进程通信,则执行器必须以特权模式运行。此外,证书目录必须指定为卷挂载
  • 可能存在这种情况:代码质量作业启动之前,DinD 服务没有完全启动。这是 Kubernetes 执行器文档中记录的限制。要解决此问题,请使用 before_script 等待 Docker 守护进程完全启动。有关示例,请参阅下面的 .gitlab-ci.yml 文件中的配置。

Kubernetes

要在 Kubernetes 中运行代码质量:

  • Docker in Docker 服务必须在 config.toml 文件中被添加为服务容器。
  • 服务容器中的 Docker 守护进程必须监听 TCP 和 UNIX 套接字,因为代码质量需要这两个套接字。
  • Docker 套接字必须通过卷共享。

由于 Docker 要求,服务容器必须启用特权标志。

[runners.kubernetes]

[runners.kubernetes.service_container_security_context]
privileged = true
allow_privilege_escalation = true

[runners.kubernetes.volumes]

[[runners.kubernetes.volumes.empty_dir]]
mount_path = "/var/run/"
name = "docker-sock"

[[runners.kubernetes.services]]
alias = "dind"
command = [
    "--host=tcp://0.0.0.0:2375",
    "--host=unix://var/run/docker.sock",
    "--storage-driver=overlay2"
]
entrypoint = ["dockerd"]
name = "docker:20.10.12-dind"
note 如果您在用极狐GitLab Runner Helm Chart,您可以在 values.yaml 文件中的 config 字段中使用上述 Kubernetes 配置。

要确保您在使用 overlay2 存储驱动,这能够提供提供最佳的总体性能:

  • 指定 Docker 命令行沟通所需的 DOCKER_HOST
  • 设置 DOCKER_DRIVER 变量为空。

使用 before_script 部分来等待 Docker 守护进程完全启动。从极狐GitLab Runner v16.9 开始,这也可以通过仅设置 HEALTHCHECK_TCP_PORT 变量来完成。

include:
  - template: Code-Quality.gitlab-ci.yml

code_quality:
  services: []
  variables:
    DOCKER_HOST: tcp://dind:2375
    DOCKER_DRIVER: ""
  before_script:
    - while ! docker info > /dev/null 2>&1; do sleep 1; done

OpenShift

对于 OpenShift 来说,您应该使用极狐GitLab Runner Operator。为了给服务容器中的 Docker 守护进程初始化其存储的权限,您必须将 /var/lib 目录作为卷挂载。

note 如果您无法将 /var/lib 目录作为卷挂载,您可以将 --storage-driver 设置为 vfs。如果您选择 vfs 值,它可能会对性能产生负面影响。

为了配置 Docker 守护进程的权限:

  1. 使用如下的配置内容创建 config.toml 文件。该配置将被用于定制极狐GitLab Runner 生成的 config.toml 文件:
[[runners]]

[runners.kubernetes]

[runners.kubernetes.service_container_security_context]
privileged = true
allow_privilege_escalation = true

[runners.kubernetes.volumes]

[[runners.kubernetes.volumes.empty_dir]]
mount_path = "/var/run/"
name = "docker-sock"

[[runners.kubernetes.volumes.empty_dir]]
mount_path = "/var/lib/"
name = "docker-data"

[[runners.kubernetes.services]]
alias = "dind"
command = [
    "--host=tcp://0.0.0.0:2375",
    "--host=unix://var/run/docker.sock",
    "--storage-driver=overlay2"
]
entrypoint = ["dockerd"]
name = "docker:20.10.12-dind"

1 为 runner 设置自定义配置

  1. 可选。将 privileged 服务帐户附加到构建 Pod。这取决于您的 OpenShift 集群设置:

    oc create sa dind-sa
    oc adm policy add-scc-to-user anyuid -z dind-sa
    oc adm policy add-scc-to-user -z dind-sa privileged
    
  2. [runners.kubernetes] 部分中设置权限。
  3. 作业定义保持与 Kubernetes 用例相同:

    include:
    - template: Code-Quality.gitlab-ci.yml
    
    code_quality:
    services: []
    variables:
      DOCKER_HOST: tcp://dind:2375
      DOCKER_DRIVER: ""
    before_script:
      - while ! docker info > /dev/null 2>&1; do sleep 1; done
    

卷和 Docker 存储

Docker 将所有的数据存储在 /var/lib 卷中,这可能会导致一个大卷。为了在集群中重用 Docker-in-Docker 存储,您可以使用持久卷 作为替代方案。