Local vs GitLab differences
Bitrab is useful because it reuses GitLab CI syntax. It is trustworthy because it does not pretend to implement all of GitLab.
Biggest difference: no container boundary
GitLab Runner commonly gives each job a fresh container or VM context. Bitrab runs jobs directly in your shell on your machine instead.12
That changes the economics and the tradeoffs:
- startup is cheaper
- local debugging is easier
- queueing disappears when you run on your workstation
- isolation is weaker
- parallel jobs can still interfere if you disable worktrees or run outside a git checkout
Support matrix
| Feature | GitLab CI | Bitrab today |
|---|---|---|
stages |
Ordered execution groups | Supported |
script, before_script, after_script |
Run in runner environment | Supported in your shell |
variables |
Runner env injection | Supported |
needs: |
DAG scheduling | Supported |
rules: if |
Conditional evaluation | Supported |
rules: exists |
File existence rules | Supported |
rules: when, allow_failure, variables, needs |
Rule-side overrides | Supported |
rules: changes |
Event-specific git comparison | Supported with local baseline semantics |
when: |
Scheduling behavior | Supported for local scheduling |
allow_failure: |
Non-blocking failures | Supported |
retry: |
Retry policy | Supported |
timeout: |
Job timeout | Supported |
artifacts: |
Persist and publish artifacts | Supported locally only |
dependencies: |
Artifact download selection | Supported locally only |
parallel: |
Fan-out jobs | Supported |
parallel: matrix: |
Matrix expansion | Supported |
extends: |
Template inheritance | Supported |
!reference |
Reuse merged configuration | Supported (nested, depth-limited) |
include: local |
Merge local config | Supported |
include: remote / include: url |
Fetch remote config | Supported; TTL cache and vendor snapshots |
include: template |
GitLab template catalog | Warned and skipped |
include: project |
Cross-project config reuse | Warned and skipped |
include: component |
CI component includes | Error |
image: |
Pull and run container image | Ignored |
services: |
Sidecar containers | Ignored |
cache: |
Shared cache semantics | Supported locally (subset; see Cache) |
workflow: rules |
Pipeline-level creation rules | Supported; skipped runs exit 3 |
trigger: |
Child or downstream pipelines | Error |
resource_group: |
Cross-run mutex | Supported with local file locks |
environment: |
Deployment metadata | Ignored |
release: |
GitLab release creation | Ignored |
pages job |
GitLab Pages deployment | Script runs, no deployment |
inputs: |
Pipeline/component inputs | Error |
only: / except: |
Legacy ref filters | Not enforced locally |
Includes
This is one place where "GitLab-like" and "GitLab-identical" are different:
- bitrab supports local includes
- bitrab can also fetch remote URL includes
bitrab vendorlocks remote includes locally;--offlinechanges resolution semantics by forbidding network fetches and requiring every remote URL to have a hash-valid snapshot- GitLab-managed include types such as
template,project, andcomponentare not available in the same way locally34
Remote includes use a transparent ten-minute cache under .bitrab/include-cache/; --no-include-cache bypasses it.
Fetches retry transient connection/read/5xx failures and reject responses larger than 5 MiB. This cache is only a
freshness optimization. In contrast, bitrab vendor is an explicit, hash-locked reproducibility boundary.
Workflow and shared resources
workflow:rules uses the same local rule evaluator as jobs. A matching when: never rule skips execution with run exit
code 3 while validation still succeeds; matching workflow variables are merged into pipeline and job variables.
Jobs sharing resource_group: acquire .bitrab/locks/<group>.lock, so they serialize across threads, worker processes,
worktrees, and concurrent bitrab invocations. Lock acquisition uses the job timeout when one is configured.
!reference resolves after includes merge and before extends, including nested list splicing and scalar lookups. Nested
references are depth-limited and circular references fail clearly.
Rules and branch context
Bitrab can evaluate local rules: expressions and exists: checks, but it does not have GitLab's full pipeline
context. That means GitLab-only variables or diff-driven logic can be absent or meaningless on your
machine.56
only: and except: are not enforced, so do not depend on them to protect a local run from deployment-style
jobs.7
Baseline selection for rules: changes
GitLab chooses a comparison ref from the pipeline event type. A local run has no push or merge-request event, so bitrab uses an explicit, conservative ladder:
--changes-base <ref>wins, followed by[tool.bitrab] changes_baseinpyproject.toml. A rule'schanges:compare_tooverrides the run default for that rule.- Otherwise bitrab computes
git merge-base HEAD <default-branch>. It detects the default branch fromorigin/HEAD, then triesorigin/main,origin/master, and localmain. - The changed set unions committed differences from that baseline, staged and unstaged edits, and untracked non-ignored files. This deliberately catches files you forgot to commit before pushing.
- Outside a git repository, or when no baseline resolves,
changes:matches with a warning. Running extra jobs is safer than silently skipping a necessary one.
Patterns are relative to the project root. * does not cross /; ** does. bitrab run --changed applies the same
change set independently of rules:, selecting jobs whose declared fingerprint or changes: patterns intersect it,
jobs with unknown inputs, and transitive needs: dependents.
Cache
Bitrab executes cache: locally: matched paths are restored into the job's working directory before
before_script and saved back after scripts, into .bitrab/cache/<key>/ under the project root (shared by
parallel worktree jobs). Supported subset:
paths:(glob patterns),key:(literal, with$VARexpansion),key: files:(max 2 files) withprefix:,policy:(pull-push/pull/push),when:(on_success/on_failure/always), a list of up to 4 cache entries, and job-level wholesale override of the top-level/defaultcache:(withcache: []/cache: {}disabling caching for a job).- Saves are atomic (staged writes published via a generation pointer) and guarded by per-key advisory locks; a lock timeout skips the cache step with a warning instead of failing the job.
bitrab run --no-cachebypasses restore and save;bitrab clean --what cachedeletes the store.
Not supported (ignored with a validation warning): untracked:, unprotect:, fallback_keys:. A
cross-runner distributed cache is meaningless locally.
Fingerprint memoization is a bitrab-only feature
GitLab always re-runs every job in a pipeline. bitrab run --incremental skips jobs whose inputs — resolved
scripts, variables, declared environment salt, input files, and upstream job fingerprints — have not changed
since their last successful local run, reporting them with a distinct cached status. The fingerprint cannot
see outside-world changes (network resources, system packages, tool upgrades); --refresh and
bitrab clean --what fingerprints are the escape hatches, and the feature is strictly opt-in. See
Incremental runs for the full contract.
Artifacts are local, not uploaded
Bitrab stores artifacts in .bitrab/artifacts/<job_name>/ and copies them between local jobs. It does not upload them
to GitLab or attach them to a pipeline record.8
Parallel execution is lighter-weight, not runner-isolated
Parallelism in bitrab is about faster execution on a single host, not strict runner isolation. For real runs in a Git
checkout, bitrab uses per-job git worktrees by default when it can. If you disable worktrees, run outside a git repo, or
choose --serial, jobs run in the project root instead.110
Mutation detection is a bitrab-only feature
GitLab does not natively warn that a supposedly read-only job rewrote part of your repository. Bitrab can.
When warn_on_mutation = true is enabled, bitrab snapshots the filesystem before each job, compares it afterward, and
reports unexpected changes outside a whitelist. Built-in cache patterns such as .pytest_cache/**, .mypy_cache/**,
__pycache__, .bitrab/**, and common coverage outputs are ignored by default.9
This is useful for keeping verify, check, and other non-mutating workflows honest.9
Validation behavior
bitrab validate combines three checks:
- GitLab schema validation
- local capability diagnostics
- structural validation of the parsed pipeline
So the tool can tell you both "this YAML is malformed" and "this YAML is valid GitLab syntax but bitrab will ignore or block part of it locally".1011
-
Source: bitrab/execution/stage_runner.py ↩↩
-
Source: bitrab/execution/job.py ↩
-
Source: bitrab/config/loader.py ↩
-
Source: bitrab/config/capabilities.py ↩
-
Source: bitrab/config/rules.py ↩
-
Source: bitrab/execution/variables.py ↩
-
Source: bitrab/plan.py ↩
-
Source: bitrab/execution/artifacts.py ↩
-
Source: bitrab/mutation.py and bitrab/execution/stage_runner.py ↩↩
-
Source: bitrab/cli.py ↩↩
-
Source: bitrab/config/validate_pipeline.py ↩