# gitlab-ci-local: the source version is 0.0.0 and the release is 4.76.0

> A tool that runs GitLab pipelines on your own machine as a shell or docker executor, distributed seven different ways, whose compiled binaries are told not to autoload a dotenv file while its Node build is not, and whose documentation devotes a ten-item section to the places a local runner cannot match a hosted one.

**firecow/gitlab-ci-local** — Tired of pushing to test your .gitlab-ci.yml?

- Repository: https://github.com/firecow/gitlab-ci-local
- Stars: 4,106 · Forks: 219
- Language: TypeScript
- License: MIT
- Published: 2026-09-23 · Updated: 2026-09-23 · Language: en
- Canonical page: https://hysenlabs.com/projects/firecow-gitlab-ci-local

## Seven install routes, and the Windows one writes into Program Files

The installation section covers seven package managers, and the Debian one is split into a preferred and a legacy form. For distributions that support the newer sources format, three commands: fetch a sources file into the apt directory, update, then install. The fallback for older distributions is the key-based method, and it carries the kind of note that only someone who has debugged apt has written: the key file must be at least the armored extension for older package managers on older Ubuntu releases, because the key itself is ASCII-armored, and the path used inside the sources list has to be changed in the command as well as in the file.

Arch goes through the AUR with any helper. macOS goes through Homebrew, with the constraint stated up front that the bash version must be at least 4. npm and Bun each get a global install line.

Windows is the outlier, because it is not a package manager at all. You install Git Bash and rsync, put the binary into the Git mingw64 bin directory, and the README gives you a single line that downloads a zip, unzips it into that directory with the space in the path escaped, and removes the archive:

```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
```

The note that follows is the one to keep: passing the variable that disables path conversion is useful in certain situations, which is a way of saying that Windows path translation will otherwise get in the way. The Debian route is the same kind of thing in a different key: a sources file, an update, then an install, with the legacy key-based form kept for distributions that predate it.

## The version in the source is a placeholder

The package manifest declares version 0.0.0 while the releases are numbered 4.x, with 4.76.0 published on 2026-09-30, 4.75.1 on 2026-08-25 and 4.75.0 on 2026-08-21. The zero is a placeholder, and the tag is the real version, which is the arrangement you would expect from a tool that stamps its version at release time rather than committing a bump for every change.

What is committed is the shape of the package. Both the module entry and the binary entry point at the same file in the build output directory, and the module type is declared as ES modules. The interesting part is that there are two different build products, and they are built two different ways.

One set of scripts compiles a single-file executable per platform, using the runtime's own compiler, with one script per target and a chain that builds all five. The x64 targets for Linux, macOS and Windows are marked with the baseline variant of the runtime and the arm64 targets are not, which is the usual way to trade a little speed for older processors. A separate script produces an ordinary Node build into a directory, and that is what the npm and Bun packages ship. So the binary you download and the package you install are two artefacts from two build paths, and the next section is about one of the ways they differ.

## The compiled binary is told not to autoload a dotenv file

Every one of the compile scripts passes a flag that turns off automatic dotenv loading. The Node build script does not pass it. That is a small line in a build configuration and a real behavioural difference between the two distribution channels, because the same tool is telling you that a file it would otherwise pick up for you is not picked up for you when you installed the compiled binary.

The convenience section has a dotenv entry in its own right, so the feature exists and is documented, which makes the flag a deliberate choice rather than an oversight. It also means a developer who has the same project installed both ways, which happens more often than anyone plans, can get different behaviour from the same configuration file.

There is a second thing in that section worth reading before the options. A note above the options list says that what you are probably looking for is the home file variables or the project file variables instead. That is a tool telling you its own headline feature is not the one you need, which is unusual and useful.

## Every option has a GCL_ variable, camel case included

Command line options can be given defaults two ways, and the first is an environment variable named after the option with a prefix:

```bash
export GCL_NEEDS=true                   # --needs options
export GCL_FILE='.gitlab-ci-local.yml'  # --file=.gitlab-ci-local.yml
```

The examples are the needs option and the file option. The naming rule is visible across the logging options, which cover timestamps, a maximum job name padding value and a quiet mode, and the middle one is the interesting case: a camel-cased option becomes a screaming snake-cased variable, so the padding option's variable name carries a separator where the option has capitals.

The second way is a file, and there are two places it can live: a dot-prefixed file in the current working directory, or one under the home directory in a subdirectory of the tool's own name. The keys in that file drop the prefix entirely, so the same two settings appear as bare names.

The point of both mechanisms is that per-developer configuration does not have to live in the repository, and the bash alias and completion lines complete it. The alias appends a short name to your shell profile, and the completion command writes its own output into the same profile, so after two appends your shell knows a short command and completes its options.

## --list prints a table with a contract for its empty cells

Sometimes you want to know what a pipeline would do before it does it, and there is a flag for that. The listing flag returns formatted output and filters out every job set to never run. The sample output has six columns, and the notes under it are the part worth having, because they define what an empty cell means.

The description column is always shown and is empty when the job has none. The allow-failure column is true, false, or a bracketed list of exit codes when specific codes are permitted to fail, which is the one case where a GitLab pipeline feature is visible in a local listing. And the needs column distinguishes absence from emptiness: omitted when unspecified, meaning the job follows stage ordering, and an empty pair of brackets when explicitly set to no dependencies, meaning the job starts immediately.

That last distinction is the kind of thing a local tool can show you and a hosted pipeline log cannot, and it is a bug you would otherwise find in production. A second flag does the same listing while including the jobs that never run, both the ones marked directly and the ones excluded implicitly by rules.

## The quirks section is the documentation that matters

The table of contents gives a Quirks section ten named subsections, and each one is a first-class heading rather than a paragraph: tracked files, local only, home file variables, remote file variables, project file variables, decorators, pipeline inputs, includes, artifacts, and self hosted custom ports.

Reading the list tells you what a local runner cannot do. Remote file variables and includes both depend on a server answering questions, so they are the two entries that describe behaviour a local run cannot reproduce at all. Artifacts and self hosted custom ports depend on the runner's environment in ways a local process does not have. Local only is a scope control rather than a limitation, and the tracked files entry is about which repository state the tool uses.

So the structure of the documentation is deliberate: the convenience section tells you how to make the tool pleasant, and the quirks section tells you where it stops being the real thing. A project that names ten of its own divergences in its table of contents is more useful than one that lists only its features, and it is the section to read before you treat a passing local run as proof that a pipeline will pass.

## Two test commands, and a Dockerfile that only installs rsync

The scripts section explains the test setup without being able to hide behind it. The plain test command runs the suite, and a second one excludes the docker-in-docker directories, which tells you the privileged cases exist and are separated from the default run. A combined check runs linting and coverage, coverage is the same suite with a flag, and type checking is a no-emit pass. There is also a start script that runs the tool against the docker-compose example, so the example is the smoke test.

The project's own Dockerfile is four lines and does one thing. It starts from a Debian slim image pinned to a 2023 base release and installs rsync. There is no command and no entry point, so it is a fixture for the docker executor tests rather than an application image, and the pinned base means the fixture will not drift with the distribution.

Around that sits the rest of the tooling: the runtime's own lockfile and configuration rather than npm's, a flat ESLint config, a dependency update bot configuration, properties for the code quality dashboard, a YAML linter configuration, an npm ignore file, and a directory whose name says it holds the Debian packaging that feeds the apt repository in the install instructions. A code of conduct, a security policy and a file for coding agents complete the top level.

## Conclusion

Use it to shorten the loop between editing a pipeline file and seeing what it does, and treat the tool as a simulator rather than a runner. Two things to know before you rely on it. The quirks section is the honest part of the documentation and its ten topics include the hosted-runner features, includes and remote file variables, that a local run cannot reproduce, so read that section before trusting a green local result. And the compiled binary and the Node build behave differently on dotenv autoloading, so a setting that works under one install path may be ignored under the other.

## FAQ

### how to install gitlab ci local

There are seven documented routes. On Debian-family distributions, fetch a Deb822 sources file into the apt directory, run an update, then install the package; older distributions get the key-based method with the note that the key file needs the armored extension. Arch uses the AUR with any helper, macOS uses Homebrew and needs bash 4 or newer, and npm and Bun each have a global install line. On Windows, install Git Bash and rsync, then download the archive and unzip the binary into the Git mingw64 bin directory.

### What does gitlab-ci-local do?

It runs GitLab pipelines locally, using either a shell executor or a docker executor, so you can exercise a pipeline file without pushing and replace the per-developer shell scripts and make files that usually grow up around that. It also offers a listing mode that prints the jobs a pipeline would add before executing anything, and default values for every command line option.

### How do I set default options for gitlab-ci-local?

Two ways. Environment variables named after each long option with a prefix, so the needs and file options become two exported variables, and a camel-cased option like the job name padding option becomes a screaming snake-cased variable name. Or a file, either one in the current working directory with a dot-prefixed name or one under the home directory in a subdirectory of the tool's own name, where the keys are the bare option names without the prefix.

### What does gitlab-ci-local --list show?

A table of the jobs a pipeline would add, with the description, stage, when, allow-failure and needs columns, filtering out every job set to never run. A second flag prints the same table while including those jobs, both the ones marked directly and the ones excluded implicitly by rules. The notes define the empty cases: needs is omitted when unspecified, meaning the job follows stage ordering, and shown as an empty pair of brackets when set to no dependencies, meaning it starts immediately.

### How does gitlab-ci-local build its binaries?

With the Bun runtime's compiler, producing a single-file executable per platform, one script per target plus a script that builds all five, where the x64 targets for Linux, macOS and Windows use the baseline variant and the arm64 targets do not. A separate script produces an ordinary Node build into a directory, and that is what the npm and Bun packages ship, so the downloaded binary and the installed package are two artefacts from two build paths.

### What are the known limitations of running GitLab CI locally?

The README keeps a Quirks section with ten named subsections: tracked files, local only, home file variables, remote file variables, project file variables, decorators, pipeline inputs, includes, artifacts, and self hosted custom ports. Two of them, remote file variables and includes, depend on a server answering questions and so cannot be reproduced locally at all, which is why that section is the one to read before treating a passing local run as proof.

## Sources

- [firecow/gitlab-ci-local on GitHub](https://github.com/firecow/gitlab-ci-local)
- [Issues](https://github.com/firecow/gitlab-ci-local/issues)
- [License: MIT](https://github.com/firecow/gitlab-ci-local/blob/master/LICENSE)
- [README](https://github.com/firecow/gitlab-ci-local/blob/master/README.md)
- [Releases](https://github.com/firecow/gitlab-ci-local/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/firecow-gitlab-ci-local
