CI/CD component examples

Test a component

Depending on a component’s functionality, testing the component might require additional files in the repository. For example, a component which lints, builds, and tests software in a specific programming language requires actual source code samples. You can have source code examples, configuration files, and similar in the same repository.

For example, the Code Quality CI/CD component’s has several code samples for testing.

Example: Test a Rust language CI/CD component

Depending on a component’s functionality, testing the component might require additional files in the repository.

The following “hello world” example for the Rust programming language uses the cargo tool chain for simplicity:

  1. Go to the CI/CD component root directory.
  2. Initialize a new Rust project by using the cargo init command.

    cargo init 

    The command creates all required project files, including a src/main.rs “hello world” example. This step is sufficient to build the Rust source code in a component job with cargo build.

    tree . ├── Cargo.toml ├── LICENSE.md ├── README.md ├── src │ └── main.rs └── templates └── build.yml 
  3. Ensure that the component has a job to build the Rust source code, for example, in 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 

    In this example:

    • The stage and rust_version inputs can be modified from their default values. The CI/CD job starts with a build- prefix and dynamically creates the name based on the rust_version input. The command cargo build --verbose compiles the Rust source code.
  4. Test the component’s build template in the project’s .gitlab-ci.yml configuration file:

    include: # include the component located in the current project from the current SHA - component: gitlab.com/$CI_PROJECT_PATH/build@$CI_COMMIT_SHA inputs: stage: build stages: [build, test, release] 
  5. For running tests and more, add additional functions and tests into the Rust code, and add a component template and job running cargo test in templates/test.yml.

    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 the additional job in the pipeline by including the test component template:

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

CI/CD component migration examples

This section shows practical examples of migrating CI/CD templates and pipeline configuration into reusable CI/CD components.

CI/CD component migration example: Go

A complete pipeline for the software development lifecycle can be composed with multiple jobs and stages. CI/CD templates for programming languages may provide multiple jobs in a single template file. As a practice, the following Go CI/CD template should be migrated.

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 
note
You can also start with migrating one job, instead of all jobs. Follow the instructions below, and only migrate the build CI/CD job in the first iteration.

The CI/CD template migration involves the following steps:

  1. Analyze the CI/CD jobs and dependencies, and define migration actions:
    • The image configuration is global, needs to be moved into the job definitions.
    • The format job runs multiple go commands in one job. The go test command should be moved into a separate job to increase pipeline efficiency.
    • The compile job runs go build and should be renamed to build.
  2. Define optimization strategies for better pipeline efficiency.
    • The stage job attribute should be configurable to allow different CI/CD pipeline consumers.
    • The image key uses a hardcoded image tag latest. Add golang_version as input with latest as default value for more flexible and reusable pipelines. The input must match the Docker Hub image tag values.
    • The compile job builds the binaries into a hard-coded target directory mybinaries, which can be enhanced with a dynamic input and default value mybinaries.
  3. Create a template directory structure for the new component, based on one template for each job.

    • The name of the template should follow the go command, for example format.yml, build.yml, and test.yml.
    • Create a new project, initialize a Git repository, add/commit all changes, set a remote origin and push. Modify the URL for your CI/CD component project path.
    • Create additional files to follow best practice: README.md, LICENSE.md, .gitlab-ci.yml, .gitignore. The following shell commands initialize the Go component structure:
    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. Create the CI/CD jobs as template. Start with the build job.
    • Define the following inputs in the spec section: stage, golang_version and binary_directory.
    • Add a dynamic job name definition, accessing inputs.golang_version.
    • Use the similar pattern for dynamic Go image versions, accessing inputs.golang_version.
    • Assign the stage to the inputs.stage value.
    • Create the binary director from inputs.binary_directory and add it as parameter to go build.
    • Define the artifacts path to 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 ]] 
    • The format job template follows the same patterns, but only requires the stage and golang_version inputs.

      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/) 
    • The test job template follows the same patterns, but only requires the stage and golang_version inputs.

      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. In order to test the component, modify the .gitlab-ci.yml configuration file, and add tests.

    • Specify a different value for golang_version as input for the build job.
    • Modify the URL for your CI/CD component path.

      stages: [format, build, test] include: - component: example.gitlab.com/$CI_PROJECT_PATH/format@$CI_COMMIT_SHA - component: example.gitlab.com/$CI_PROJECT_PATH/build@$CI_COMMIT_SHA - component: example.gitlab.com/$CI_PROJECT_PATH/build@$CI_COMMIT_SHA inputs: golang_version: "1.21" - component: example.gitlab.com/$CI_PROJECT_PATH/test@$CI_COMMIT_SHA inputs: golang_version: latest 
  6. Add Go source code to test the CI/CD component. The go commands expect a Go project with go.mod and main.go in the root directory.

    • Initialize the Go modules. Modify the URL for your CI/CD component path.

      go mod init example.gitlab.com/components/golang 
    • Create a main.go file with a main function, printing Hello, CI/CD component for example. Tip: Use code comments to generate Go code using GitLab Duo Code Suggestions.

      // 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") } 
    • The directory tree should look as follows:

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

Follow the remaining steps in the converting a CI/CD template into a component section to complete the migration:

  1. Commit and push the changes, and verify the CI/CD pipeline results.
  2. Follow documentation best practices to update the README.md and LICENSE.md files.
  3. Release the component and verify it in the CI/CD catalog.
  4. Add the CI/CD component into your staging/production environment.

The GitLab-maintained Go component provides an example for a successful migration from a Go CI/CD template, enhanced with inputs and component best practices. You can inspect the Git history to learn more.