Orchestrating GitLab and TeamCity Integration

The intersection of GitLab and JetBrains TeamCity represents a sophisticated synergy between a comprehensive DevOps platform and a high-performance continuous integration server. While GitLab provides an integrated ecosystem for source code management and built-in CI/CD, TeamCity offers an alternative, highly robust build automation engine capable of handling complex build runners and diverse version control system (VCS) requirements. Integrating these two powerhouses allows organizations to leverage the granular project management and repository hosting of GitLab while utilizing the advanced build orchestration capabilities of TeamCity. However, this integration is not without its complexities, ranging from OAuth authentication failures in self-managed environments to the intricacies of triggering builds based on Merge Request events and the challenge of synchronizing external CI pipelines with native GitLab-CI stages.

The Architecture of GitLab and TeamCity Interconnectivity

The integration between GitLab and TeamCity is designed to facilitate a seamless flow of data from the point of code commitment to the final build artifact. TeamCity acts as the build runner, which monitors the GitLab repository for changes. When a trigger condition is met—such as a push to a specific branch or the creation of a Merge Request—TeamCity initiates a build process.

The communication between these systems typically happens through two primary channels: the Git protocol (via SSH or HTTPS) for source code retrieval and the GitLab API for metadata, authentication, and status reporting. In a standard configuration, TeamCity uses a VCS Root to define how it connects to the GitLab repository. This connection can be established via SSH keys, which are often the most reliable method for automated systems, or via HTTPS using personal access tokens or OAuth.

When utilizing the GitLab CE/EE (Community Edition or Enterprise Edition) integration, the systems exchange tokens to enable features like the "Sign in to GitLab" functionality within the TeamCity interface. This allows TeamCity to programmatically browse available repositories and manage permissions without requiring manual URL entry for every project.

OAuth Authentication and Connection Failures in Self-Managed Environments

A critical point of failure in the integration of self-managed GitLab CE/EE installations and TeamCity is the OAuth handshake process. In many enterprise environments, GitLab is deployed via an Omnibus installation, which includes an integrated NGINX proxy. This proxy layer can introduce complexities in how requests are routed and how timeouts are handled.

One documented failure manifests as a 504 (teamcity_invalid_response) error during the "Sign in to GitLab" process. This specific status code indicates a Gateway Timeout, meaning the NGINX proxy did not receive a timely response from the upstream GitLab application server when attempting to hit the /oauth/token endpoint.

The process for configuring this connection involves several precise steps:

  • In TeamCity, the administrator navigates to project settings, selects connections, and adds a GitLab CE/EE connection.
  • A Callback URL is generated by TeamCity, such as https://xxx-xxx-webbuild01.xx.xxxxxxxxxxxxxxxxx.local/oauth/gitlab/accessToken.html.
  • In GitLab, the administrator navigates to settings, then applications, and adds the aforementioned Redirect URL, ensuring the "api" scope is selected.
  • The Application ID and Secret generated by GitLab are then copied back into the TeamCity configuration along with the GitLab base URL (e.g., https://gitlab.prod.com/).

When this configuration fails with a 504 error, it is often because the connection between the TeamCity server and the GitLab NGINX proxy is interrupted or the proxy is not configured to handle the specific request headers required by the OAuth flow. Interestingly, users often find that while the API is accessible via a browser or command line (CLT), the programmatic request from the TeamCity server fails. This suggests a discrepancy in how the TeamCity server interacts with the NGINX proxy compared to a standard browser.

Furthermore, there is a distinct difference in connectivity based on the protocol used. In some failure scenarios, the system may successfully run builds if the Fetch URL is manually entered as an SSH path (e.g., [email protected]:project/test.git) using uploaded SSH keys, while failing completely when using HTTPS URLs (e.g., https://gitlab.prod.com/project/test.git) or standard login/password authentication. This indicates that the authentication failure is specifically tied to the OAuth/HTTPS layer rather than a general network blockage.

Merge Request Triggering and Behavioral Anomalies

The integration of TeamCity with GitLab is intended to automate the validation of code through Merge Request (MR) triggers. Ideally, a build should be triggered the moment an MR is created to ensure that the proposed changes do not break the existing codebase.

However, inconsistencies have been observed in how GitLab triggers these builds. In a specific bug scenario, the following behavior occurs:

  • The TeamCity build is configured with a GitLab VCS root using a branch specification that includes +:refs/(merge-requests/*)/head and +:refs/heads.(*).
  • The JetBrains TeamCity CI integration is enabled specifically for Merge Request triggers, while the Push trigger is disabled.
  • When a new branch is created from master and a Merge Request is subsequently created, the TeamCity build is not triggered.
  • If a new commit is pushed to the branch after the MR has been created, the build is triggered.
  • If the description of the Merge Request is updated, the build is triggered.

This behavior indicates a failure in the initial "Merge Request Created" event hook between GitLab and TeamCity. The "Push" and "Update" events are functioning correctly, but the specific event associated with the creation of the MR is either not being sent by GitLab or not being correctly interpreted by TeamCity. For the end user, this means the first line of defense (the initial MR check) is bypassed, requiring a manual trigger or a subsequent commit to initiate the CI pipeline.

Advanced Pipeline Synchronization and External CI Integration

In complex DevOps workflows, organizations may use a hybrid approach where some tasks are handled by native GitLab-CI and others by an external server like TeamCity. This creates a challenge: the external pipeline appears as a stage in GitLab, but it is not natively linked to the GitLab-CI stage execution logic.

A common requirement is the need to query artifacts, such as code coverage reports, from a TeamCity build before the final GitLab-CI stage can proceed. Because the external TeamCity pipeline does not "block" the GitLab-CI pipeline in a standard way, developers must implement custom polling mechanisms within their .gitlab-ci.yml configuration.

The following architectural setup demonstrates how to handle this:

  • Define stages in .gitlab-ci.yml such as scan, test, external, and final.
  • Use a specific image (e.g., aquasec/trivy) for security scanning in the scan stage.
  • Implement a read_coverage job in the final stage that actively polls the TeamCity API.

To achieve this synchronization, a script is used to loop until the TeamCity build status is no longer 404 (not found) and is instead finished. This is done using a curl command that targets the TeamCity REST API:

bash while [ $STATUS -eq 404 ]; do sleep 10 STATUS=$(curl -Is --header "Authorization: Bearer ${TC_ACCESS_TOKEN}" ${TC_BASE_URL}/app/rest/builds/branch:${CI_COMMIT_BRANCH},revision:${CI_COMMIT_SHA},state:finished,lookupLimit:500/status | head -n 1|cut -d$' ' -f2) done

In this scenario, the TC_ACCESS_TOKEN and TC_BASE_URL must be configured as protected variables in GitLab. This method manually bridges the gap between the two systems, ensuring that the GitLab pipeline does not finalize until the external TeamCity build has completed and its artifacts are available for retrieval.

Migration Strategies: Moving GitLab and TeamCity to the Cloud

As organizations scale, they often move their on-premise build infrastructure to cloud-based Kubernetes environments, such as Azure. This transition involves migrating GitLab, TeamCity, and artifact repositories like Nexus.

The primary challenges in such a migration are often networking-related. For instance, some organizations run GitLab on virtual machines specifically to avoid the complexity of SSH networking within OpenShift or Kubernetes. Moving to a Kubernetes-based setup requires a careful design to maintain "zero-downtime" and ensure that end users experience no change in URLs or ports.

Key constraints for a successful cloud migration include:

  • Network Access: Restricting access exclusively to the company network.
  • User Experience: Maintaining identical URLs and ports to prevent breaking existing CI/CD pipelines.
  • Reliability: Implementing zone-redundant architectures to ensure high availability.
  • Efficiency: Utilizing hosted offerings where they make sense to reduce the operational burden.

By moving these services into a Kubernetes environment, organizations can leverage the ability to perform zero-downtime upgrades and more efficient resource sharing across different services.

Integration Management via Group API

For administrators managing multiple projects, GitLab provides a Group Integrations API that allows for the programmatic oversight of connected CI tools. The GET /groups/:id/integrations endpoint provides a detailed list of all active integrations within a group.

The following table describes the typical properties returned by the Group Integrations API for a CI tool like Jenkins or TeamCity:

Property Type Description
id Integer Unique identifier of the integration
title String The display name of the integration (e.g., "Jenkins CI")
slug String The internal identifier for the integration type
active Boolean Indicates if the integration is currently enabled
commit_events Boolean Whether commit events trigger the integration
push_events Boolean Whether push events trigger the integration
mergerequestsevents Boolean Whether MR events trigger the integration
pipeline_events Boolean Whether pipeline events trigger the integration
vulnerability_events Boolean Available only for GitLab Enterprise Edition

The ability to toggle merge_requests_events and push_events via the API allows administrators to dynamically control which events trigger external builds in TeamCity, providing a layer of control above the individual project settings.

Conclusion: A Technical Analysis of the GitLab-TeamCity Ecosystem

The integration between GitLab and TeamCity is a powerful mechanism for enterprises that require the sophisticated build orchestration of JetBrains and the flexible version control of GitLab. However, the "last mile" of this integration—specifically authentication and event triggering—is where most technical friction occurs.

The 504 Gateway Timeout errors experienced in self-managed GitLab environments highlight a critical dependency on the NGINX proxy configuration. When TeamCity attempts to authenticate via OAuth, the request must traverse the proxy and reach the GitLab application server within a specific timeframe. The fact that SSH-based connections often bypass these issues confirms that the problem lies in the HTTP/HTTPS application layer of the GitLab Omnibus installation.

Furthermore, the gap in Merge Request trigger reliability suggests a potential desynchronization between the GitLab webhook event and the TeamCity trigger listener. While "push" and "update" events are reliable, the "creation" event for MRs appears unstable in certain versions, necessitating a workaround where developers must push a dummy commit to kickstart the build.

Finally, the necessity of using custom curl polling scripts to synchronize GitLab-CI with external TeamCity pipelines demonstrates that while "integrations" exist, they are often "loose." A true deep integration would allow GitLab-CI to natively wait for an external TeamCity status update before proceeding to the next stage. Until such native functionality is fully matured, the use of REST API polling remains the only viable method for ensuring artifact integrity and pipeline sequence.

Sources

  1. GitLab Issue 360787
  2. GitLab Issue 327452
  3. GitLab Forum - Integration External CI Tools
  4. Cloudflight Engineering - Moving GitLab to Azure
  5. GitLab API - Group Integrations
  6. Octopus Docs - TeamCity Build Servers

Related Posts