GitLab Pipeline Triggering and API Orchestration via cURL

The ability to programmatically interact with the GitLab CI/CD ecosystem using cURL allows developers to move beyond static pipeline definitions into the realm of dynamic orchestration. By leveraging the GitLab API v4, users can trigger pipelines across project boundaries, automate the creation of releases, and validate configuration files through linting services. This capability is fundamental for implementing complex deployment workflows, such as those where a change in a shared library project must automatically trigger a downstream build in a dependent application project.

At the core of this functionality is the Pipeline Trigger Token. Unlike a personal access token, which provides broad access to a user's account, a trigger token is a specialized secret designed specifically to initiate pipelines. This separation of concerns ensures that external systems or other projects can start a build process without having the permission to modify source code or manage project settings. When integrated into a .gitlab-ci.yml file, cURL transforms a standard job into a powerful dispatcher capable of communicating with the GitLab REST API to manage the lifecycle of software delivery.

Pipeline Triggering Mechanisms

Triggering a pipeline via cURL can be achieved through two primary methods: direct API calls using the POST method and the use of webhooks for event-driven execution. Both methods rely on the Project ID, which is a unique numerical identifier displayed on the project overview page, and a trigger token.

Using cURL for API-Driven Triggers

The most direct way to start a pipeline is by sending a POST request to the trigger endpoint. Depending on the requirements, the trigger token and the reference (branch or tag) can be passed as form data or as query string parameters.

For a standard multi-line cURL command using form data, the syntax is as follows:

bash curl --request POST \ --form token=<token> \ --form ref=<ref_name> \ "https://gitlab.example.com/api/v4/projects/<project_id>/trigger/pipeline"

Alternatively, for scenarios where the request is being constructed in a environment that prefers URL parameters, the following syntax is used:

bash curl --request POST \ "https://gitlab.example.com/api/v4/projects/<project_id>/trigger/pipeline?token=<token>&ref=<ref_name>"

In these examples, the following placeholders must be replaced:

  • https://gitlab.example.com must be replaced with https://gitlab.com or the specific URL of the self-managed instance.
  • <token> must be replaced with the actual pipeline trigger token.
  • <ref_name> must be replaced with the target branch or tag name, such as main.
  • <project_id> must be replaced with the project's unique ID (e.g., 123456).

Webhook Integration

Webhooks provide a way to trigger pipelines based on specific events, such as push or tag events from another project. The webhook URL follows a specific structure:

https://gitlab.example.com/api/v4/projects/<project_id>/ref/<ref_name>/trigger/pipeline?token=<token>

When utilizing webhooks, the <ref_name> provided in the URL takes precedence over the ref_name contained within the actual webhook payload. This allows administrators to force the pipeline to run on a specific branch regardless of which branch triggered the event in the source repository. It is critical to note that if the <ref_name> contains slashes, it must be URL-encoded to avoid request failures.

Advanced Pipeline Configuration and Variable Injection

A simple trigger is often insufficient for production environments. GitLab allows for the injection of CI/CD variables and structured inputs during the API call to modify the behavior of the triggered pipeline.

Passing CI/CD Variables

To pass variables that the triggered pipeline can use, the variables[key]=value format is employed. These variables have the highest precedence and will override any existing variables with the same name defined in the project settings or the .gitlab-ci.yml file.

Example of a cURL request with variable injection:

bash curl --request POST \ --form token=TOKEN \ --form ref=main \ --form "variables[UPLOAD_TO_S3]=true" \ "https://gitlab.example.com/api/v4/projects/123456/trigger/pipeline"

From a security perspective, these variables are visible on the job's page in the triggered pipeline, but access is restricted; only users with the Owner or Maintainer role can view the actual values.

Utilizing Pipeline Inputs

For improved security and flexibility, GitLab supports pipeline inputs. Unlike generic variables, inputs provide a structured method to parameterize pipelines with built-in validation and documentation. These are passed using the inputs[name]=value format.

Example of triggering with pipeline inputs:

bash curl --request POST \ --form token=TOKEN \ --form ref=main \ --form "inputs[environment]=production" \ "https://gitlab.example.com/api/v4/projects/123456/trigger/pipeline"

The values passed to these inputs are validated against the spec:inputs section of the target pipeline's configuration. For instance, a configuration might look like this:

yaml spec: inputs: environment: type: string description: "Deployment environment" options: [dev, staging, production] default: dev

This ensures that the API call cannot trigger a pipeline with an invalid environment name, reducing the risk of deployment errors.

Cross-Project Pipeline Orchestration

One of the most powerful use cases for cURL in GitLab CI is triggering a pipeline in one project (Project B) based on an event in another project (Project A).

Implementation Example

To trigger a pipeline on the main branch of Project B when a tag is created in Project A, the following job must be added to Project A's .gitlab-ci.yml:

yaml trigger_pipeline: stage: deploy script: - 'curl --fail --request POST --form token=$MY_TRIGGER_TOKEN --form ref=main "${CI_API_V4_URL}/projects/123456/trigger/pipeline"' rules: - if: $CI_COMMIT_TAG environment: production

In this configuration:
- The rules section ensures the job only executes when a tag is pushed to Project A.
- 123456 represents the Project ID of Project B.
- $MY_TRIGGER_TOKEN is a masked CI/CD variable containing the secret token for Project B.
- The --fail flag in cURL ensures that the job fails if the API returns an error, preventing silent failures in the orchestration chain.

Handling the Triggered Pipeline Environment

Once a pipeline is triggered via the API or a webhook, the resulting pipeline can be configured to behave differently than a standard manual or push-triggered pipeline.

Accessing Webhook Payloads

When a pipeline is triggered via a webhook, the data sent by the triggering system is available through the TRIGGER_PAYLOAD predefined CI/CD variable. This variable is exposed as a file-type variable. To access the content of the payload within a script, the following command is used:

bash cat $TRIGGER_PAYLOAD

Controlling Job Execution

To determine which jobs should run in a triggered pipeline, GitLab provides the $CI_PIPELINE_SOURCE variable. The preferred method for controlling this is via the rules keyword.

$CIPIPELINESOURCE value only/except keywords Trigger method
trigger triggers Pipelines triggered with the pipeline triggers API using a trigger token

While the only and except keywords are still available, the use of rules is the current recommended practice for defining job execution logic.

Security Considerations and Token Management

The security of the trigger token is paramount, as a leaked token could allow an unauthorized actor to force unscheduled deployments or attempt to extract sensitive CI/CD variables.

Token Protection

To mitigate risk, trigger tokens should always be stored as masked CI/CD variables. Masking ensures that the token does not appear in the job logs.

Revocation Process

If a token is suspected of being compromised, it must be revoked immediately. The process is as follows:

  1. Navigate to the project in the top bar search.
  2. Select Settings > CI/CD from the left sidebar.
  3. Expand the Pipeline triggers section.
  4. Select Revoke next to the compromised token.

It is important to note that once a token is revoked, it cannot be restored; a new token must be generated and updated across all triggering systems.

Special API Use Cases: Releases and Linting

Beyond triggering pipelines, cURL is often used to interact with other GitLab API endpoints, such as the Releases API and the CI Lint API.

Automating Releases via cURL

Creating a release can be attempted via a POST request to the releases endpoint. However, users often encounter issues with JSON formatting and variable expansion. An example of a structured release job using the curlimages/curl image is:

yaml release_job: stage: deploy image: curlimages/curl:latest script: - | curl --header 'Content-Type: application/json' --header "JOB-TOKEN: $CI_JOB_TOKEN" \ --data "tag_name=${CI_COMMIT_TAG}" \ --request POST "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/releases" - echo "Created release $CI_COMMIT_TAG" rules: - if: $CI_COMMIT_TAG

In this scenario, specifying the Content-Type: application/json header is critical for the API to correctly interpret the data. If users experience "Bad Request" errors, an alternative is to use the GitLab Release CLI tool (image: registry.gitlab.com/gitlab-org/release-cli:latest), although some versions may be deprecated.

Linting .gitlab-ci.yml via API

The GitLab CI Lint API allows users to validate their configuration files. A common failure point when using cURL for linting is sending the YAML content directly without wrapping it in the JSON structure expected by the endpoint.

A failing approach often looks like this:

```bash

!/usr/bin/env bash

PAYLOAD=$( cat << JSON
{ "content":
$(<$PWD/../.gitlab-ci.yml)
JSON
)
curl --include --show-error --request POST --header "Content-Type: application/json" --header "Accept: application/json" "https://gitlab.com/api/v4/ci/lint" --data-binary "$PAYLOAD"
```

The {"status":400,"error":"Bad Request"} error typically occurs because the API expects the .gitlab-ci.yml content to be properly converted to a JSON-compatible string within the content field of the request body. For those requiring a robust solution for linting, the gitlab-lint-client Ruby gem is available via gem install gitlab-lint-client, which provides a CLI tool glab-lint and a pre-commit hook called validate-gitlab-ci.

Conclusion

The integration of cURL within GitLab CI/CD transforms the platform from a simple automation tool into a sophisticated orchestration engine. By mastering the use of the Pipeline Trigger API, users can implement complex, multi-project dependencies and dynamic environment injections. The shift from basic variables to structured pipeline inputs further enhances the reliability of these triggers by enforcing validation at the API level.

Security remains the primary concern when implementing these workflows. The reliance on trigger tokens necessitates a strict adherence to masking and a proactive revocation strategy. While the API provides immense power, the nuances of header specifications—such as the requirement for Content-Type: application/json when creating releases or the specific JSON wrapping required for the CI linting endpoint—demonstrate that successful implementation requires precise formatting. Whether using raw cURL commands for lightweight triggers or specialized tools like the Release CLI and gitlab-lint-client for complex tasks, the ability to interact with GitLab's API is essential for any advanced DevOps practice.

Sources

  1. GitLab Documentation - Pipeline Triggers
  2. GitLab Forum - curl post request in ci-cd pipeline
  3. GitLab Forum - example of how to use api to send curl request from file
  4. GitLab Forum - curl request ci lint gitlab-ci-yml

Related Posts