Skip to content

feat(PipelinesService): add support for pipeline inputs to CreatePipeline and RunPipelineTrigger

Summary

This MR adds support for the inputs parameter when creating pipelines through both the standard API (CreatePipeline) and trigger tokens (RunPipelineTrigger). Pipeline inputs allow passing typed parameters to pipelines that use the spec:inputs keyword in their .gitlab-ci.yml configuration.

What does this MR do?

  • Adds a new PipelineInputsOption type to represent pipeline inputs with compile-time type safety
  • Adds Inputs field to both CreatePipelineOptions and RunPipelineTriggerOptions
  • Uses generics and type constraints to ensure only valid types can be used at compile time
  • Supports the following input types as per GitLab API requirements:
    • string
    • integers (int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64)
    • floats (float32, float64)
    • bool
    • string arrays ([]string)

Why not reuse the existing PipelineInput type?

The package already has a PipelineInput type that was added in #2122 (closed) for pipeline schedules:

type PipelineInput struct {
    Name  string `json:"key"`
    Value any    `json:"value"`
}

This type cannot be reused for CreatePipeline and RunPipelineTrigger because the API endpoints expect different JSON structures:

  • Pipeline schedules expect an array of objects:

    "inputs": [
      {"key": "environment", "value": "production"},
      {"key": "replicas", "value": 3}
    ]
  • CreatePipeline/RunPipelineTrigger expect a flat object:

    "inputs": {
      "environment": "production",
      "replicas": 3
    }

The GitLab API uses these different formats for different contexts, so we need separate types to properly serialize the data for each endpoint.

Implementation Decision

Two approaches were considered for implementing pipeline inputs:

1. Runtime validation with map[string]any (Considered)

Use a simple map type and validate types during JSON marshaling:

type PipelineInputOptions map[string]any

// MarshalJSON validates types at runtime
func (i PipelineInputOptions) MarshalJSON() ([]byte, error) {
    // Check each value's type, return error if invalid
}

Pros:

  • Simple, declarative API
  • Minimal code changes
  • Familiar map syntax

Cons:

  • Type errors only discovered at runtime
  • No IDE autocomplete or compile-time hints about valid types
  • Requires error handling for type validation failures

2. Compile-time type safety with generics (Chosen)

Use generics to create a type-safe wrapper that ensures only valid types can be used:

Inputs: PipelineInputsOption{
    "environment": NewPipelineInputValue("production"),
    "replicas":    NewPipelineInputValue(3),
    "debug":       NewPipelineInputValue(false),
    "regions":     NewPipelineInputValue([]string{"us-east", "eu-west"}),
}

Pros:

  • Compile-time type safety - invalid types cannot compile
  • Clear API contract through type constraints
  • IDE autocomplete shows exactly what types are supported
  • Consistent with existing Ptr() pattern in the library
  • No runtime type errors possible

Cons:

  • Requires wrapping each value with NewPipelineInputValue()
  • Slightly more verbose than plain map syntax
  • More complex implementation using generics and interfaces

Chosen because: The compile-time safety guarantees outweigh the minor verbosity. This approach prevents entire classes of runtime errors and provides better developer experience through IDE support. The wrapping pattern is already familiar to users through the existing Ptr() function used throughout the library.

Example Usage

// Using CreatePipeline
pipeline, _, err := client.Pipelines.CreatePipeline(projectID, &CreatePipelineOptions{
    Ref: Ptr("main"),
    Inputs: PipelineInputsOption{
        "environment": NewPipelineInputValue("production"),
        "replicas":    NewPipelineInputValue(3),
        "debug":       NewPipelineInputValue(false),
        "regions":     NewPipelineInputValue([]string{"us-east", "eu-west"}),
    },
})

// Using RunPipelineTrigger
pipeline, _, err := client.PipelineTriggers.RunPipelineTrigger(projectID, &RunPipelineTriggerOptions{
    Token: Ptr("trigger-token"),
    Ref:   Ptr("main"),
    Variables: map[string]string{
        "CI_VAR": "value",
    },
    Inputs: PipelineInputsOption{
        "environment": NewPipelineInputValue("staging"),
        "replicas":    NewPipelineInputValue(2),
    },
})

Testing

Both changes include comprehensive table-driven tests that:

  • Cover all supported input types
  • Verify compile-time type checking prevents invalid types
  • Test correct JSON serialization
  • Also improve test coverage for existing functionality (variables)

Related Issues

Fixes: #2154 (closed)
References: #2122 (closed) (introduced PipelineInput for pipeline schedules)

References

Edited by Florian Forster

Merge request reports

Loading