The Premise
You describe GitLab configuration in C#. The compiler ensures it is valid. The runtime renders it back to Ruby. No hand-written GITLAB_OMNIBUS_CONFIG strings. No typos discovered five minutes into container startup. No wondering whether nginx['proxy_set_headers'] still exists in the version you're deploying.
GitLab.DockerCompose is a Docker Compose service contributor that wraps GitLab Omnibus's 500+ configuration settings in strongly-typed C# models, generated at build time from ~80 versioned gitlab.rb templates by a Roslyn Source Generator. It follows the same three-phase pipeline -- Design, Build, Runtime -- established by the Docker Compose Bundle and the Traefik Bundle.
Before and After
| Raw GITLAB_OMNIBUS_CONFIG | Typed Builder API | |
|---|---|---|
| Format | Unescaped Ruby inside YAML inside docker-compose.yml | Fluent C# with IntelliSense |
| Validation | Container startup (3-5 minutes later) | Compile time (instant) |
| Discoverability | Browse 90KB gitlab.rb or docs | Autocomplete with XML doc comments |
| Version awareness | Manual cross-reference with release notes | [SinceVersion] / [UntilVersion] attributes |
| Escaping | Quotes inside quotes inside YAML | Type system handles it |
| Refactoring | Find-and-replace in strings | Rename symbol, compiler catches all references |
The Pipeline
Design time fetches gitlab.rb templates from the GitLab Tags API -- one file per stable CE release, latest patch per minor version. Build time parses each template, merges all versions into a unified superset with version metadata, and emits C# config classes with fluent builders. Runtime uses the typed builder API, renders back to Ruby syntax, and injects the result into Docker Compose service definitions via the contributor pattern.
Part I: The Problem
Why GITLAB_OMNIBUS_CONFIG is broken. The raw string approach, what goes wrong with typos and version-specific settings, the 500+ settings across 55 Ruby prefixes, and the vision for compile-time safety.
Part II: The Three-Phase Pipeline
The Design/Build/Runtime pipeline pattern shared with Docker Compose Bundle and Traefik Bundle. Project structure, cost model, and why three phases beat the alternatives.
Part III: Design Time -- Downloading gitlab.rb
The design-time tool that queries the GitLab Tags API, filters stable CE releases, and downloads versioned gitlab.rb templates. Rate limiting, version filtering, and the resource file convention.
Part IV: The Source Generator -- Parsing Ruby, Merging Versions
The Roslyn incremental source generator: regex-based Ruby parsing, the 55 prefix groups, version merging with SinceVersion/UntilVersion tracking, and C# class emission. The densest part of the series.
Part V: Generated Models and Builders
What the source generator produces: typed config classes per Ruby prefix, the root GitLabOmnibusConfig aggregator, fluent builder classes, version metadata attributes, and the IntelliSense experience.
Part VI: Rendering -- From C# Back to Ruby
GitLabRbRenderer: converting populated C# config objects back to valid Ruby syntax. Type mapping, escaping, standalone URLs, and the bridge from typed models to GITLAB_OMNIBUS_CONFIG.
Part VII: The Five Contributors
The five Docker Compose service contributors -- GitLab, PostgreSQL, Redis, GitLab Runner, and MinIO. Each contributor's service definition, volumes, healthchecks, and how they compose together via the IComposeFileContributor pattern.
Part VIII: Testing, Comparison, and Philosophy
Testing strategy for rendering and contributors. Comparison with raw YAML, Ansible, Helm, and Pulumi. The design philosophy: typed configuration, contributor composition, Omnibus pragmatism, Traefik integration, and health-first orchestration.