GitLab CI Local

The orchestration of continuous integration and continuous delivery (CI/CD) typically resides within the cloud or a remote server environment, where a central authority manages the execution of build pipelines. However, the emergence of GitLab CI Local shifts this paradigm by allowing developers to mirror the remote runner environment on their own machines. This tool provides a local bridge to execute jobs defined in .gitlab-ci.yml without the need to commit code and push it to a remote server just to verify if a script works. By leveraging Docker containers, GitLab CI Local ensures that the environment where a job runs locally is nearly identical to the environment provided by a remote GitLab Runner, thereby reducing the "it works on my machine" friction and accelerating the development loop.

The fundamental value proposition of GitLab CI Local is feature parity. The goal is to maintain a workflow where minimal to no changes are required in the primary .gitlab-ci.yml file to achieve successful execution on both a remote runner and a local workstation. This allows developers to iterate on complex pipeline configurations—such as defining stages, managing dependencies between jobs, and handling environment variables—without clogging the remote GitLab server with dozens of "test" or "fix CI" commits.

Architectural Overview and the CI Pipeline Mechanism

GitLab CI operates on a job-based system. These jobs are defined within YAML files, specifically the .gitlab-ci.yml file, which serves as the blueprint for the entire pipeline. In a standard GitLab environment, these jobs are executed inside containers, ensuring a clean and reproducible state for every run.

The pipeline structure is organized into batches of jobs. These batches are grouped into stages. When a pipeline triggers, jobs within a specific batch can be executed in parallel, provided they do not have dependencies on one another. This parallelism is critical for large-scale projects, such as the ns-3 project, where multiple compilers and package versions must be validated simultaneously to ensure compatibility. For example, in the ns-3 project repository, the CI files are located in ns-3-dev/utils/tests/, with the primary configuration residing in gitlab-ci.yml. The jobs in such a project are designed to automate builds, execute tests, handle packaging, and manage distribution.

Comprehensive Installation Procedures Across Platforms

GitLab CI Local is designed to be cross-platform, supporting various operating systems through multiple installation vectors.

Linux Distributions

For users on Ubuntu Focal or similar Debian-based systems, the tool can be installed via a dedicated Personal Package Archive (PPA). This process involves importing the GPG key to ensure the integrity of the package.

The following commands are used for setup:

bash PPA_KEY_PATH=/etc/apt/sources.list.d/gitlab-ci-local-ppa.asc curl -s "https://gitlab-ci-local-ppa.firecow.dk/pubkey.gpg" | sudo tee "${PPA_KEY_PATH}" echo "deb [ signed-by=${PPA_KEY_PATH} ] https://gitlab-ci-local-ppa.firecow.dk ./" | sudo tee /etc/apt/sources.list.d/gitlab-ci-local.list sudo apt-get update sudo apt-get install gitlab-ci-local

It is critical to note that the file /etc/apt/sources.list.d/gitlab-ci-local.list is explicitly referenced. If a user modifies the path during the initial commands, they must also manually update the content within that list file to maintain consistency.

For Arch Linux users, the tool is available through the Arch User Repository (AUR). Installation can be handled via an AUR helper such as paru:

bash paru -S gitlab-ci-local

macOS and Windows Environments

On macOS, the tool is easily accessible via Homebrew:

bash brew install gitlab-ci-local

For Windows users, the recommended approach is to place the binary within the Git Bash environment to ensure compatibility with the Unix-like shell. The binary can be downloaded and installed using the following sequence:

bash curl -L https://github.com/firecow/gitlab-ci-local/releases/latest/download/gitlab-ci-local-windows-amd64.zip -o gcl.zip && unzip -o gcl.zip -d /c/Program\ Files/Git/mingw64/bin && rm gcl.zip

Language-Specific Package Managers

The tool is also distributed via Node.js and Bun package managers, allowing for a global installation regardless of the host OS:

bash npm install -g gitlab-ci-local

bash bun install -g gitlab-ci-local

A technical requirement for the execution of these installations is that the system's bash version must be 4.x.x or higher.

Command Line Interface and Job Management

The gitlab-ci-local CLI provides a suite of tools to inspect and execute pipelines. One of the most critical functions is the ability to visualize which jobs are available for execution before actually starting the pipeline.

The command gitlab-ci-local --list generates a formatted output of all available jobs. This list filters out jobs that are explicitly set to when: never, meaning they will not appear in the standard list.

When using the --list command, the output typically includes the following columns:

Column Description
name The identifier of the job
description A detailed explanation of the job (empty if not set)
stage The pipeline stage the job belongs to
when The condition for execution (e.g., on_success)
allow_failure Whether the job can fail without stopping the pipeline
needs Dependencies that must be completed before this job starts

The allow_failure field can be a boolean (true/false) or a specific list of exit codes, such as [42,137], indicating that the job is allowed to fail if the process exits with those specific codes. The needs field determines the execution order; if it is omitted, the job follows the standard stage ordering. If it is set to [], the job has no dependencies and starts immediately.

To execute a specific job locally, the user provides the job name as an argument:

bash gitlab-ci-local some-job

Environment Variable Configuration and Local Overrides

A common challenge in local CI testing is the presence of "Secret" or "Protected" variables that are usually stored in the GitLab server's CI/CD settings and are not committed to the repository for security reasons. GitLab CI Local solves this by utilizing local variable files.

Variable File Implementation

To define variables locally, users can create a file named .gitlab-ci-local-variables.yml (or variables.yml in specific project structures). For example, if a job in .gitlab-ci.yml utilizes a variable called $SOME_TEXT, the local variable file should be configured as follows:

yaml SOME_TEXT: "hello from the other side!"

Global Configuration Paths

Depending on the user's permissions and the environment, the location of the variables file varies:

  • Standard User: ~/.gitlab-ci-local/variables.yml
  • Root User (Docker): /root/.gitlab-ci-local/variables.yml

A typical global configuration for specifying the default branch would look like this:

yaml global: CI_DEFAULT_BRANCH: "master"

Advanced Configuration and Performance Tuning

GitLab CI Local allows for extensive customization through environment variables and CLI flags. These configurations can be placed in a .gitlab-ci-local-env file in the current working directory or in the global $HOME/.gitlab-ci-local/.env file.

Operational Environment Variables

Users can define the following exports to modify the behavior of the tool:

  • GCL_NEEDS=true: Enables the --needs option for dependency management.
  • GCL_FILE='.gitlab-ci-local.yml': Specifies a custom filename for the local CI configuration instead of the default.
  • GCL_TIMESTAMPS=true: Displays timestamps in the logs, which is essential for debugging performance bottlenecks.
  • GCL_MAX_JOB_NAME_PADDING=30: Limits the padding around job names in the console output to keep the logs clean.
  • GCL_QUIET=true: Suppresses all job output, providing a cleaner terminal for automated scripts.

For those using Windows with MSYS2 or Git Bash, executing the tool with the following flag can be necessary to avoid path conversion issues:

bash gitlab-ci-local --variable MSYS_NO_PATHCONV=1

Shell Integration and Aliasing

To streamline the workflow, users are encouraged to add an alias and enable shell completion in their .bashrc file:

bash echo "alias gcl='gitlab-ci-local'" >> ~/.bashrc gitlab-ci-local --completion >> ~/.bashrc

Comparative Analysis: Local Execution vs. Self-Hosted Runner

It is important to distinguish between using gitlab-ci-local and setting up a self-hosted GitLab Runner on a local server.

Self-Hosted Runner Scenario

In a self-hosted setup, a user installs the GitLab server (often via Docker) and then must install a separate "GitLab Runner" to act as the "hands" of the operation. Without the Runner, the GitLab server (the "brain") will show pipelines in a "pending" status because there is no available agent to execute the code.

The setup for a self-hosted project typically involves:

bash cd project_folder git init --initial-branch=main git remote add origin http://localhost:8080/root/ci-test.git git add . git commit -m "add: test CI" git push --set-currentPage origin main

In this scenario, the .gitlab-ci.yml is pushed to the server, and the server manages the job.

Local Tool Scenario

Conversely, gitlab-ci-local does not require a connection to a GitLab server to run a job. It parses the .gitlab-ci.yml file directly from the local filesystem and spins up the required Docker containers to execute the script. This eliminates the latency of pushing code and waiting for a runner to pick up the job, making it an ideal tool for the "inner loop" of development.

Practical Implementation Example

To illustrate the workflow, consider a simple pipeline defined in .gitlab-ci.yml:

```yaml
image: debian:latest
stages:
- some-stage

some-job:
stage: some-stage
script:
- echo "$SOME_TEXT"
```

To execute this locally, the developer would follow these steps:

  1. Install Docker and gitlab-ci-local.
  2. Create a .gitlab-ci-local-variables.yml file containing SOME_TEXT: "local test".
  3. Run the job using the command gitlab-ci-local some-job.

The tool will pull the debian:latest image, inject the SOME_TEXT variable into the container, and execute the echo command, outputting "local test" to the terminal.

Conclusion

GitLab CI Local transforms the CI/CD experience by decentralizing the execution of pipeline jobs. By providing a high-fidelity local simulation of the GitLab Runner, it allows developers to validate complex YAML configurations, test shell scripts, and verify environment variable injections without the overhead of remote server interaction. The tool's ability to handle job dependencies via the needs keyword, its flexible installation across Linux, macOS, and Windows, and its robust support for local variable overrides ensure that it integrates seamlessly into professional DevOps workflows. While a self-hosted GitLab Runner is necessary for final integration and continuous monitoring, gitlab-ci-local is the superior tool for the iterative phase of pipeline development, effectively bridging the gap between local coding and remote deployment.

Related Posts