# Pipelines

# Terminology

  • Runner or Gitlab Runner is the server on which the CI commands are executed.
  • Runner executor is how the runner executes commands.
  • Pipeline is a set of stages through which a project is built or tested.
  • Job is some action in the Pipeline, e.g. the launch of unit tests.
  • Stage is a Pipeline stage that can simultaneously execute some jobs.
  • CI / CD is shortened from Continuous Integration and Continuous Deployment.

# Connecting the project to CI / CD

Read official documentation (opens new window).

# General recommendations

  1. Recommended folder for all CI/CD related files is ci/.
  2. If for the tests launch you need to deploy the database or any other services use [services] (https://docs.gitlab.com/ee/ci/docker/using_docker_images.html#define-image-and-services-from-gitlab-ciyml (opens new window))
  3. Do not use Makefile to run commands in gitlab-ci. Makefile is used only as a developer tool.
  4. If you need to write conditions or a multi-line script, move the code into a separate bash file and put it inci / bin /.
  5. If you need to transfer artifacts from one stage to another, use artifacts anddependencies.
  6. Be sure to set a reasonable expires_in for artifacts. At least for a few hours. Documentation (opens new window).
  7. Write stage and job names in lowercase snake style (opens new window).

# Tags

  • docker - container execution, documentation (opens new window).
  • shell - execution in a shell. You can use only those utilities that were preinstalled for the runner.
  • ansible - if you need to run Ansible.
  • ruby - if ruby is required. We recommend using docker with theimage: ruby declaration.
  • sourceguardian - server with sourceguardian.
  • ioncube - server with ioncube.

# Multiplatform image build

Build images for both x64 and ARM CPU architectures:

build:
  tags: [docker]
  image: docker:20.10.22-dind
  services:
    - docker:dind
  variables:
    DOCKER_HOST: tcp://docker:2376
    DOCKER_CERT_PATH: /certs/client
    DOCKER_TLS_VERIFY: 1
  scripts:
    - docker login -u "$CI_DEPLOY_USER" -p "$CI_DEPLOY_PASSWORD" "$CI_REGISTRY" || true
    - export VERSION=$(cat VERSION | tr -d "[:space:]")
    - export MAJOR_VERSION=$(echo $VERSION | cut -d. -f1)
    - export MINOR_VERSION=$(echo $VERSION | cut -d. -f2)
    - docker context create tls-environment
    - docker buildx create --name multiarch-builder --driver docker-container --use tls-environment
    - docker buildx build --platform=linux/arm64,linux/amd64
      --tag "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME"
      --tag "$CI_REGISTRY_IMAGE:$VERSION"
      --tag "$CI_REGISTRY_IMAGE:$MAJOR_VERSION"
      --tag "$CI_REGISTRY_IMAGE:${MAJOR_VERSION}.${MINOR_VERSION}"
      --tag "$CI_REGISTRY_IMAGE:latest"
      --file "ci/Dockerfile"
      --build-arg PHP_VERSION=${PHP_VERSION} "."
      --push
      --provenance=false

# Push images to private docker registry

  1. Authorization

    before_script: docker login -u gitlab-ci-token -p ${CI_JOB_TOKEN} ${CI_REGISTRY};

CI_JOB_TOKEN and CI_REGISTRY - are already available (opens new window) when running a pipeline in CI / CD.

  1. Publish with a tag containing the full address of the image, doc (opens new window):

    docker build -t GITLAB_REGISTRY:1443/group/subgroup/project . docker push GITLAB_REGISTRY:1443/group/subgroup/project

# How to push git tag on CI / CD launch

Gitlab has not yet implemented (opens new window) a mechanism for creating tags via CI, but you can use the API:

#!/usr/bin/env bash
set -e -x -o pipefail

curl -X POST --show-error --fail "https://${CI_SERVER_HOST}/api/v4/projects/${CI_PROJECT_ID}/repository/tags?ref=${CI_COMMIT_SHA}&tag_name=${APP_VERSION}&private_token=${GITLAB_TOKEN}"
  • GITLAB_URL - GitLab URL.
  • CI_PROJECT_ID - project ID. Forwarded by Gitlab CI itself.
  • CI_COMMIT_SHA - commits SHA. Forwarded by Gitlab CI itself.
  • APP_VERSION - product or deployment version.
  • GITLAB_TOKEN - Personal Access Tokens. It is advisable not to use your own token, but create the token for the Bot user.

# DRY

# How to improve pipeline throughput

  1. Run independent jobs in parallel simultaneously:

    stages:

    • checks

    check_1: stage: checks script: ... check_2: stage: checks script: ...

  2. Use cache (opens new window) and artifacts/dependencies (opens new window). Cache to cache the result and reuse it in the same job. Artifacts - for using the result in other jobs.

  3. Use parallel (opens new window) when possible.

  4. Add interruptible: true to the jobs.

  5. Find jobs that depend on the same artifact and combine them to one.

# How to replace entrypoint in image

White empty entrypoint:

image:
  name: "${APP_IMAGE}"
  entrypoint: [ "" ]

# How to debug failed jobs?

Read the logs

Go to a Project > CI / CD > Jobs

Try debug mode

  1. Add to script on failed job sleep 10000.
  2. Run pipeline.
  3. Press Debug button on the running job page.

Debug ENVs

Add to the .gitlab-ci.yml:

do_something:
  variables:
    CI_DEBUG_TRACE: "true"

Run gitlab-runner locally

You can run gitlab-runner locally, but it has a number of things to mention:

  1. The pod must be committed, because checkout is done during the execution.
  2. extends are not supported.
  3. Global (before|after)_script, image, services are not supported.
  4. Local cache is not supported. Connection to minio/s3 (opens new window) can be configured.

Installation https://docs.gitlab.com/runner/install (opens new window)

Then you can run jobs locally.

An example with docker-executor:

gitlab-runner exec docker do_something

An example with shell-executor:

gitlab-runner exec shell do_something

# Static Analyzers, dep scanners, and linters

Check Pipeline components (opens new window) for additional images.

# Golangci-lint (*.go)

See shared-tools repo for .golang-ci.yml configuration file.

golangci_lint:
  stage: quality_checks
  tags: [docker]
  image: golangci/golangci-lint
  script:
    - golangci-lint run -v -c .golang-ci.yml

# DependencyCheck (*.go)

TODO: jeremylong/DependencyCheck (opens new window).

# Yamllint (*.yaml)

See shared-tools repo for .yamllint.yaml configuration file.

yaml_lint:
  stage: quality_checks
  tags: [docker]
  image: registry.gitlab.com/pipeline-components/yamllint:0.19.3
  script:
    - yamllint .

# Styelint (*.css, *.scss)

Stylelint (opens new window)

stylelint:
  stage: quality_checkss
  tags: [docker]
  image: registry.gitlab.com/pipeline-components/stylelint:latest
  script:
    - stylelint --color 'src/**/*.(css|sass|scss)'

# NPM audit

NPM Audit (opens new window).

npm_audit:
  stage: quality_checks
  tags: [docker]
  image: node:16
  script:
    - npm audit --audit-level=high
  cache:
    paths:
      - node_modules
  dependencies:
    - npm

# Rubycop (*.rb)

rubocop:
  stage: quality_checks
  tags: [docker]
  image: GITLAB_DOMAIN:1443/apliteni/third-party/ruby:rubocop
  script:
    - rubocop -E -P

Replace GITLAB_DOMAIN.

# Remark-lint (*.md)

remark-lint:
  stage: quality_checks
  tags: [docker]
  image: GITLAB_DOMAIN:1443/apliteni/third-party/remark-lint:latest
  script:
    - remark --no-stdout --color .

Replace GITLAB_DOMAIN.

# Textlint (*.md)

textlint:
  stage: quality_checks
  tags: [docker]
  image: GITLAB_DOMAIN:1443/apliteni/third-party/textlint:latest
  script:
    - .

Replace GITLAB_DOMAIN.

# bundler-audit

TODO: stage example with rubysec/bundler-audit.

# Psalm (*.php)

TODO stage example with psalm

# local-php-security-checker (*.php)

TODO: stage example with local-php-security-checker (opens new window).

# Tslint (*.ts)

TODO

# Trivy (docker, kubernetes)

TODO: stage example with [Trivy][https://github.com/aquasecurity/trivy].

# Snyk (.)

TODO: stage example with Snyk (opens new window) code vulnerability scanning.