General guidelines

Based on experience, the guidelines below apply to all uses of GitLab CICD, regardless of whether in a library project.

Know the pipeline types

GitLab CICD runs three distinct types of pipelines:

  • Branch pipelines, which run by default on any push that changes a branch, as well as most manual and scheduled pipeline runs
  • Tag pipelines, which run by default on any push that changes a tag
  • Merge Request pipelines which run under some conditions on pushes to a branch managed by a merge request, as well as manual pipeline runs from the merge request form

Without attention, a project can end up with multiple pipelines running on the same push, pipelines triggering other pipelines in a loop, or simple confusion. The patterns described under pipeline conditions help to simplify things.

Leverage stages for visual flow and dependency openness

Both needs: and stage: can be used to define the order in which jobs run. Some guidelines:

  • Without needs: or dependencies: a job runs when all the previous stages finish, and gets all the artifacts from all previous stages
  • If a job has any needs: at all, it will run when the needed jobs are done, potentially out of stage order - also then only gets artifacts from listed jobs
  • Use dependencies: to optimize artifact downloads (rarely necessary)
  • needs: can be used within a stage, but can look a little confusing in the web UI (where jobs are sorted alphabetically by default)
  • stage: allows control over how jobs are grouped together in both the pipeline page and the pipelines list
  • Define meaningful stages and let the artifacts flow to take advantage of the setenv/getenv pattern

Use base jobs to avoid global defaults

In CICD YAML, the globally-defined image: keyword is deprecated in favour of the default: keyword. Just keep in mind that the default: will apply to every job in the pipeline that doesn't define it's own image (or control defaults). Once the use of GitLab CICD becomes more sophisticated, with imports: and components, the global settings become confusing. Instead, use base jobs (called hidden jobs in the documentation) to reuse configuration. A simple example:


.maven:
  image: maven:3.8.7-openjdk-18-slim
  variables:
    MAVEN_CLI_OPTS: >-
      -Dmaven.repo.local=.m2/repository
  cache:
    paths:
      - .m2/repository
    key: maven

maven-build:
  extends: [.maven]
  script:
    - mvn build ${MAVEN_CLI_OPTS}

maven-deploy:
  extends: [.maven]
  ...

Avoid Git pushes in pipelines

I advise against performing Git operations (other than tagging) in a CICD pipeline, for several reasons:

  • The Git protocol is designed for source code repositories; CICD operations happen strictly downstream
  • GitLab's Job artifacts, Package registry, and Container registry capabilities provide managed, access-control mechanisms to store downstream information, avoiding the need to commit files such as build products back into a Git repository
  • GitLab's Releases and Environments capabilities provide mechanisms to track, control, and roll back deployments, avoiding the need for a "release tracking branch"
  • Without careful attention to detail, it's possible to accidentally set up CICD loops, where a Git push triggers a CICD run, which triggers another Git push, etc

In the event a Git push operation seems necessary, the operation would require either a deploy key with "Read-write" permission or an access token with write_repository permission. Access tokens expire (maximum expiration period of a year) and must be rotated. Read the documentation in detail before selecting and implementing an approach.