Expand (some) CI variables lazily and selectively

Problem

In GitLab the only time we need to build the full set of CI variables is when sending them to the runner as we assign a job to execute. In all other scenarios we use variables to expand strings potentially containing other variables.

To expand even a single variable in a string we build a lot of variables.

job:
  script: echo
  rules:
    - if: $CI_COMMIT_BRANCH 

To expand $CI_COMMIT_BRANCH we first build all variables that can potentially be used in that given context.

Can we lazily build only CI_COMMIT_BRANCH and memoize its value?

Updated Proposal (March 2026)

The original proposal assumed that predefined variables could not be overridden by user-defined variables (dependency on #388961 (closed)). However, #388961 (closed) was closed in 2023 after user feedback that overriding predefined variables is necessary for debugging and testing.

Hybrid lazy evaluation approach:

Instead of making ALL variables lazy, we use a hybrid approach:

  1. Load user-defined variables eagerly (project/group/instance variables) - Required to maintain correct variable precedence - Includes database query and decryption
  2. Make expensive predefined variables lazy - Only evaluate when actually accessed - Focus on variables with expensive operations
  3. Keep cheap predefined variables eager - Variables that are just property access don't benefit from lazy evaluation - Lambda overhead isn't worth the complexity

Good candidates for lazy evaluation:

Gitaly calls:

  • CI_COMMIT_MESSAGE, CI_COMMIT_TITLE, CI_COMMIT_DESCRIPTION - loads commit data from Gitaly
  • CI_COMMIT_TAG_MESSAGE - calls repository.find_tag
  • CI_COMMIT_AUTHOR, CI_COMMIT_TIMESTAMP, CI_COMMIT_USER_LOGIN - Git commit metadata

Database queries:

  • CI_MERGE_REQUEST_APPROVED - queries approval_rules and approvers tables
  • CI_KUBERNETES_ACTIVE - queries deployment_platforms + may make API call
  • CI_DEPLOY_FREEZE - queries freeze_periods table
  • CI_EXTERNAL_PULL_REQUEST_* - loads external_pull_requests from database
  • CI_OPEN_MERGE_REQUESTS - queries merge_requests table with permissions check

Not good candidates:

  • CI_COMMIT_BRANCH, CI_PIPELINE_SOURCE, CI_PROJECT_PATH - just property access
  • These should remain eager as the lambda overhead outweighs any benefit
Edited by Laura Montemayor