Zum Inhalt

Pipeline Manual Deployment Gating

Einordnung ins Overtime-Projekt

Nicht jede Umgebung von Overtime soll gleich leicht erreichbar sein: Die Testumgebung darf mit wenig Reibung befüllt werden, die Produktionsumgebung braucht dagegen ein bewusstes menschliches Freigabe-Gate. Dieses ADR beschreibt, wie das in der Pipeline technisch erzwungen wird.


1. Kontext & Problemstellung (Deutsch)

Deployments in produktive Cluster sollten immer eine bewusste, gezielte Entscheidung sein und nicht vollautomatisch im Hintergrund ablaufen. Allerdings unterscheiden sich die Anforderungen je nach Umgebung im fiktiven B2B-Kollaborations-Projekt Overtime fundamental: Entwickler:innen benötigen einen reibungslosen, schnellen Zugriff auf die Testumgebung, um in der Entwicklung befindliche Features agil zu validieren.

Die Produktionsumgebung hingegen darf ausschließlich von verifiziertem, freigegebenem Code auf geschützten Branches oder über signierte Release-Tags erreichbar sein. Ein vollautomatisches "Deploy-on-Push" in die Produktion würde das menschliche Freigabe-Gate (Vier-Augen-Prinzip) umgehen.


2. Architecture Decision Record (English)

ADR-010: Pipeline Manual Deployment Gating

Status

Accepted

Context and Problem Statement

Deployments should be intentional, not automatic. However, the operational constraints differ per environment: engineers need frictionless access to the testing environment to validate in-progress work, while production must only be reachable from known-good code on a protected branch or a signed release tag. A fully automated deploy-on-push to production would bypass the human sign-off that compliance frameworks require.

Decision Drivers

  • Frictionless Testing: Any in-progress feature branch must be deployable with a single click to support exploratory testing.
  • Strict Production Gating: Only code on a protected branch (main after successful review) or a signed semantic release tag may be deployed. This gate must be strictly enforced by GitLab server-side, not just by convention.
  • Explicit Intent: All deployments must be manually triggered — no implicit push-to-deploy mechanisms.
  • Non-Blocking Test Deploys: Failed or skipped test deployments must not block the remaining stages of the pipeline (such as semantic releases).

Considered Options

  • Automatic deploy on push — Fast feedback loop but lacks a human gate, making it entirely unsuitable for production deployments.
  • Separate deployment pipelines — Triggered manually after the primary build pipeline completes. This increases the amount of required clicks and makes tracing which build maps to which deployment significantly harder. Also the GitLab UI is weird in that regard.
  • Manual buttons inside the existing pipeline — Utilizing when: manual jobs in the same pipeline as the build and scan steps. Provides a one-click deployment experience with full traceability back to the originating commit.

Decision Outcome

Chosen: Manual deploy buttons (when: manual) inside the existing pipeline, with per-environment access rules strictly enforced via GitLab's $CI_COMMIT_REF_PROTECTED server-side variable.

Access Control Summary
Any branch  →  [▶ deploy-to-testing]                 Always visible, manual
v*.*.* tag  →  [▶ deploy-tag-to-testing]             Always visible, manual
v*.*.* tag  →  [▶ deploy-tag-to-production]          Visible only on protected tags
(+ protected)

Consequences & Trade-offs

Positive Consequences
  • High Developer Velocity: Developers on a feature branch can deploy to testing immediately without ceremony or separate pipeline triggers.
  • Hard Environment Boundaries: The production deployment button is physically absent from unprotected branch pipelines — it cannot be accidentally triggered or bypassed.
  • Complete Traceability: Every deployment job explicitly links back to the exact originating pipeline (commit SHA, pipeline URL) via the automated update-gitops.yml commit message inside the GitOps repository.
Negative Consequences / Trade-offs
  • No Continuous Delivery to Prod: Requiring a manual click means true Continuous Delivery (CD) to production is omitted by design. A human operator must explicitly authorize the deployment after every release and chose the environment explicitly.
  • Silent Configuration Failures: If the GitLab protected tag rule (v*) is missing or configured incorrectly, the production deployment button on tag pipelines will disappear silently without throwing an error message.

3. Pipeline Implementation & Code Snippets (English)

The following YAML snippet (overtime-ci-templates/deploy-environments.yml) defines the deployment gating logic. It utilizes GitLab's parallel: matrix feature to centralize the environment list. Adding a new target cluster simply requires adding its name to the matrix array.

# Central environment matrix — the only place machines are listed.
#
# To bring a new machine online:
#   Testing:    add its overlay name to TARGET_ENV under deploy-to-testing
#               and deploy-tag-to-testing
#   Production: same for deploy-to-production / deploy-tag-to-production,
#               then protect the new environment in GitLab (see ENVIRONMENTS.md)
#
# App repos include this file and set IMAGE_NAME + OVERLAY_PATH as pipeline
# variables. No deploy job definitions are needed in app repos.

# ==========================================
# Branch / MR pipelines
# ==========================================

deploy-to-testing:
  extends: .update-gitops
  variables:
    IMAGE_TAG: $CI_COMMIT_SHORT_SHA
  parallel:
    matrix:
      - TARGET_ENV: [overtime-staging] # could add here more testing clusters
  environment:
    name: testing/$TARGET_ENV
  needs: [sign-image]
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
      when: manual
      allow_failure: true
    - if: "$CI_COMMIT_BRANCH"
      when: manual
      allow_failure: true

# ==========================================
# Tag pipelines (Semantic Release)
# ==========================================

deploy-tag-to-testing:
  extends: .update-gitops
  variables:
    IMAGE_TAG: $CI_COMMIT_TAG
  parallel:
    matrix:
      - TARGET_ENV: [overtime-staging]
  environment:
    name: testing/$TARGET_ENV
  needs: [sign-release-image]
  rules:
    - if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/'
      when: manual

deploy-tag-to-production:
  extends: .update-gitops
  variables:
    IMAGE_TAG: $CI_COMMIT_TAG
  parallel:
    matrix:
      - TARGET_ENV: [overtime-production]
  environment:
    name: production/$TARGET_ENV
  needs: [sign-release-image]
  rules:
    - if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/ && $CI_COMMIT_REF_PROTECTED == "true"'
      when: manual

4. Compliance Einordnung (Deutsch)

Das detaillierte Mapping auf BSI IT-Grundschutz, CRA und CIS Benchmarks wird später ergänzt.