{{< details >}}

  • Tier: 基础版,专业版,旗舰版
  • Offering: JihuLab.com,私有化部署

{{< /details >}}

测试组件

根据组件的功能,测试组件可能需要在仓库中添加额外的文件。例如,使用特定编程语言进行代码检查、构建和测试的软件组件需要实际的源代码示例。您可以在同一个仓库中放置源代码示例、配置文件及类似内容。

示例:测试 Rust 语言 CI/CD 组件

根据组件的功能,测试组件可能需要在仓库中添加额外的文件。

以下 Rust 编程语言的“Hello World”示例简化地使用了 cargo 工具链:

  1. 进入 CI/CD 组件根目录。
  2. 使用 cargo init 命令初始化一个新的 Rust 项目。

    cargo init
    

    该命令创建了所有必需的项目文件,包括一个 src/main.rs 的“Hello World”示例。此步骤足以在组件任务中使用 cargo build 构建 Rust 源代码。

    tree
    .
    ├── Cargo.toml
    ├── LICENSE.md
    ├── README.md
    ├── src
    │   └── main.rs
    └── templates
        └── build.yml
    
  3. 确保组件有一个任务来构建 Rust 源代码,例如在 templates/build.yml 中:

    spec:
      inputs:
        stage:
          default: build
          description: 'Defines the build stage'
        rust_version:
          default: latest
          description: 'Specify the Rust version, use values from https://hub.docker.com/_/rust/tags Defaults to latest'
    ---
    
    "build-$[[ inputs.rust_version ]]":
      stage: $[[ inputs.stage ]]
      image: rust:$[[ inputs.rust_version ]]
      script:
        - cargo build --verbose
    

    在这个示例中:

    • stagerust_version 输入可以从其默认值进行修改。CI/CD 任务以 build- 前缀开始,并根据 rust_version 输入动态创建名称。命令 cargo build --verbose 编译 Rust 源代码。
  4. 在项目的 .gitlab-ci.yml 配置文件中测试组件的 build 模板:

    include:
      # include the component located in the current project from the current SHA
      - component: $CI_SERVER_FQDN/$CI_PROJECT_PATH/build@$CI_COMMIT_SHA
        inputs:
          stage: build
    
    stages: [build, test, release]
    
  5. 为运行测试及更多内容,添加额外的函数和测试到 Rust 代码中,并在 templates/test.yml 中添加一个组件模板和任务运行 cargo test

    spec:
      inputs:
        stage:
          default: test
          description: 'Defines the test stage'
        rust_version:
          default: latest
          description: 'Specify the Rust version, use values from https://hub.docker.com/_/rust/tags Defaults to latest'
    ---
    
    "test-$[[ inputs.rust_version ]]":
      stage: $[[ inputs.stage ]]
      image: rust:$[[ inputs.rust_version ]]
      script:
        - cargo test --verbose
    
  6. 通过包含 test 组件模板在流水线中测试额外的任务:

    include:
      # include the component located in the current project from the current SHA
      - component: $CI_SERVER_FQDN/$CI_PROJECT_PATH/build@$CI_COMMIT_SHA
        inputs:
          stage: build
      - component: $CI_SERVER_FQDN/$CI_PROJECT_PATH/test@$CI_COMMIT_SHA
        inputs:
          stage: test
    
    stages: [build, test, release]
    

CI/CD 组件模式

本节提供实现常见模式的 CI/CD 组件的实用示例。

使用布尔输入有条件地配置任务

您可以通过结合 boolean 类型输入和 extends 功能来构建具有两个条件的任务。

例如,使用 boolean 输入配置复杂缓存行为:

spec:
  inputs:
    enable_special_caching:
      description: 'If set to `true` configures a complex caching behavior'
      type: boolean
---

.my-component:enable_special_caching:false:
  extends: null

.my-component:enable_special_caching:true:
  cache:
    policy: pull-push
    key: $CI_COMMIT_SHA
    paths: [...]

my-job:
  extends: '.my-component:enable_special_caching:$[[ inputs.enable_special_caching ]]'
  script: ... # run some fancy tooling

此模式通过将 enable_special_caching 输入传递到任务的 extends 关键字来工作。根据 enable_special_cachingtrue 还是 false,从预定义的隐藏任务中选择适当的配置(.my-component:enable_special_caching:true.my-component:enable_special_caching:false)。

使用 options 有条件地配置任务

您可以使用多个选项组合任务,其行为类似于 ifelseif 条件。使用 extendsstring 类型的多个 options 来进行任意数量的条件。

例如,使用 3 种不同的选项配置复杂的缓存行为:

spec:
  inputs:
    cache_mode:
      description: Defines the caching mode to use for this component
      type: string
      options:
        - default
        - aggressive
        - relaxed
---

.my-component:cache_mode:default:
  extends: null

.my-component:cache_mode:aggressive:
  cache:
    policy: push
    key: $CI_COMMIT_SHA
    paths: ['*/**']

.my-component:cache_mode:relaxed:
  cache:
    policy: pull-push
    key: $CI_COMMIT_BRANCH
    paths: ['bin/*']

my-job:
  extends: '.my-component:cache_mode:$[[ inputs.cache_mode ]]'
  script: ... # run some fancy tooling

在此示例中,cache_mode 输入提供了 defaultaggressiverelaxed 选项,每个选项对应于一个不同的隐藏任务。通过使用 extends: '.my-component:cache_mode:$[[ inputs.cache_mode ]]' 扩展组件任务,任务根据所选选项动态继承正确的缓存配置。

CI/CD 组件迁移示例

本节展示了将 CI/CD 模板和流水线配置迁移到可重用 CI/CD 组件的实用示例。

CI/CD 组件迁移示例:Go

完整的软件开发生命周期流水线可以由多个任务和阶段组成。编程语言的 CI/CD 模板可能在单个模板文件中提供多个任务。作为实践,以下 Go CI/CD 模板应该被迁移。

default:
  image: golang:latest

stages:
  - test
  - build
  - deploy

format:
  stage: test
  script:
    - go fmt $(go list ./... | grep -v /vendor/)
    - go vet $(go list ./... | grep -v /vendor/)
    - go test -race $(go list ./... | grep -v /vendor/)

compile:
  stage: build
  script:
    - mkdir -p mybinaries
    - go build -o mybinaries ./...
  artifacts:
    paths:
      - mybinaries

{{< alert type=”note” >}}

您也可以开始迁移一个任务,而不是所有任务。按照下面的说明,在第一次迭代中仅迁移 build CI/CD 任务。

{{< /alert >}}

CI/CD 模板迁移涉及以下步骤:

  1. 分析 CI/CD 任务和依赖关系,并定义迁移操作:
    • image 配置是全局的,需要移到任务定义中
    • format 任务在一个任务中运行多个 go 命令。为了提高流水线效率,go test 命令应该移到一个单独的任务中。
    • compile 任务运行 go build,应该重命名为 build
  2. 定义优化策略以提高流水线效率。
    • stage 任务属性应该是可配置的,以允许不同的 CI/CD 流水线消费者。
    • image 键使用了硬编码的镜像标签 latest。添加 golang_version 作为输入,默认值为 latest,以便更灵活和可重用的流水线。输入必须匹配 Docker Hub 镜像标签值。
    • compile 任务将二进制文件构建到一个硬编码的目标目录 mybinaries,这可以通过动态输入和默认值 mybinaries 来增强。
  3. 为新组件创建模板目录结构,每个任务一个模板。

    • 模板的名称应该遵循 go 命令,例如 format.ymlbuild.ymltest.yml
    • 创建一个新项目,初始化一个 Git 仓库,添加/提交所有更改,设置远程 origin 并推送。修改您的 CI/CD 组件项目路径的 URL。
    • 创建其他文件,如编写组件指导中所述:README.mdLICENSE.md.gitlab-ci.yml.gitignore。以下 shell 命令初始化 Go 组件结构:
    git init
    
    mkdir templates
    touch templates/{format,build,test}.yml
    
    touch README.md LICENSE.md .gitlab-ci.yml .gitignore
    
    git add -A
    git commit -avm "Initial component structure"
    
    git remote add origin https://gitlab.example.com/components/golang.git
    
    git push
    
  4. 将 CI/CD 任务创建为模板。从 build 任务开始。
    • spec 部分定义以下输入:stagegolang_versionbinary_directory
    • 添加动态任务名称定义,访问 inputs.golang_version
    • 使用类似的模式进行动态 Go 镜像版本,访问 inputs.golang_version
    • 将阶段分配给 inputs.stage 值。
    • inputs.binary_directory 创建二进制目录并将其作为参数添加到 go build
    • 定义产物路径为 inputs.binary_directory

      spec:
        inputs:
          stage:
            default: 'build'
            description: 'Defines the build stage'
          golang_version:
            default: 'latest'
            description: 'Go image version tag'
          binary_directory:
            default: 'mybinaries'
            description: 'Output directory for created binary artifacts'
      ---
      
      "build-$[[ inputs.golang_version ]]":
        image: golang:$[[ inputs.golang_version ]]
        stage: $[[ inputs.stage ]]
        script:
          - mkdir -p $[[ inputs.binary_directory ]]
          - go build -o $[[ inputs.binary_directory ]] ./...
        artifacts:
          paths:
            - $[[ inputs.binary_directory ]]
      
    • format 任务模板遵循相同的模式,但只需要 stagegolang_version 输入。

      spec:
        inputs:
          stage:
            default: 'format'
            description: 'Defines the format stage'
          golang_version:
            default: 'latest'
            description: 'Golang image version tag'
      ---
      
      "format-$[[ inputs.golang_version ]]":
        image: golang:$[[ inputs.golang_version ]]
        stage: $[[ inputs.stage ]]
        script:
          - go fmt $(go list ./... | grep -v /vendor/)
          - go vet $(go list ./... | grep -v /vendor/)
      
    • test 任务模板遵循相同的模式,但只需要 stagegolang_version 输入。

      spec:
        inputs:
          stage:
            default: 'test'
            description: 'Defines the format stage'
          golang_version:
            default: 'latest'
            description: 'Golang image version tag'
      ---
      
      "test-$[[ inputs.golang_version ]]":
        image: golang:$[[ inputs.golang_version ]]
        stage: $[[ inputs.stage ]]
        script:
          - go test -race $(go list ./... | grep -v /vendor/)
      
  5. 为了测试组件,修改 .gitlab-ci.yml 配置文件,并添加测试

    • build 任务的输入指定一个不同的 golang_version 值。
    • 修改您的 CI/CD 组件路径的 URL。

      stages: [format, build, test]
      
      include:
        - component: $CI_SERVER_FQDN/$CI_PROJECT_PATH/format@$CI_COMMIT_SHA
        - component: $CI_SERVER_FQDN/$CI_PROJECT_PATH/build@$CI_COMMIT_SHA
        - component: $CI_SERVER_FQDN/$CI_PROJECT_PATH/build@$CI_COMMIT_SHA
          inputs:
            golang_version: "1.21"
        - component: $CI_SERVER_FQDN/$CI_PROJECT_PATH/test@$CI_COMMIT_SHA
          inputs:
            golang_version: latest
      
  6. 添加 Go 源代码以测试 CI/CD 组件。go 命令期望在根目录中有一个 go.modmain.go 的 Go 项目。

    • 初始化 Go 模块。修改您的 CI/CD 组件路径的 URL。

      go mod init example.jihulab.com/components/golang
      
    • 创建一个 main.go 文件,包含一个主函数,打印 Hello, CI/CD component 作为示例。您可以使用代码注释生成 Go 代码,使用极狐GitLab Duo 代码建议

      // Specify the package, import required packages
      // Create a main function
      // Inside the main function, print "Hello, CI/CD Component"
      
      package main
      
      import "fmt"
      
      func main() {
        fmt.Println("Hello, CI/CD Component")
      }
      
    • 目录结构应如下所示:

      tree
      .
      ├── LICENSE.md
      ├── README.md
      ├── go.mod
      ├── main.go
      └── templates
          ├── build.yml
          ├── format.yml
          └── test.yml
      

按照将 CI/CD 模板转换为组件部分中的剩余步骤完成迁移:

  1. 提交并推送更改,并验证 CI/CD 流水线结果。
  2. 按照编写组件的指导更新 README.mdLICENSE.md 文件。
  3. 发布组件,并在 CI/CD 目录中验证。
  4. 将 CI/CD 组件添加到您的测试/生产环境中。