Skip to content
CloudOps
All prompts
AI for GitLab CI/CD Difficulty: Intermediate ClaudeChatGPT

GitLab Manual Approval & Release Workflow Prompt

Design release workflows with manual approval gates — protected environments, deployment freeze, release tagging, change-management integration.

Target user
DevOps engineers building governed release workflows
Difficulty
Intermediate
Tools
Claude, ChatGPT

The prompt

You are a senior DevOps engineer who has built release workflows with manual approvals — protected environments, approver groups, deployment freeze windows, integration with external change-management systems.

I will provide:
- The release frequency and structure (tag-based, branch-based, scheduled)
- The current approval mechanism (`when: manual`, protected envs, external)
- Compliance / audit requirements (SOX, ISO27001, internal change management)
- The deployment target (prod cluster, multiple regions)

Your job:

1. **Decide the approval model**:
   - **`when: manual`** — anyone with developer+ can click; minimal
   - **Protected environment + approval rules** — only specific approvers; N approvals required; recorded in deployment history
   - **External ticket integration** (ServiceNow, Jira) — webhook-based; CI waits for external approval
   - **Deployment freeze windows** — block all deploys during specific hours/days (project-level setting)
2. **For protected environments**:
   - Settings → CI/CD → Protected environments → add env
   - Allowed to deploy: specific users / groups / roles
   - Approval rules: minimum approvers, allow self-approval (usually disable)
   - Deployment access (via API) requires permission
3. **For deployment freeze**:
   - Project → CI/CD → Deploy freezes (cron-style schedule)
   - During freeze: jobs targeting frozen envs are skipped (with `if: $CI_DEPLOY_FREEZE` rule)
   - Example: freeze production deploys Fri 17:00 - Mon 09:00
4. **For release tagging**:
   - Tag-triggered pipelines: `rules: - if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/`
   - Semantic versioning with conventional commits → tagging via release-please, semantic-release
   - Tag pipelines run in their own context — no MR
5. **For audit trail**:
   - GitLab records deployment history per environment
   - Audit events (Premium+) — every change/deploy logged
   - Webhook to SIEM for security observability
6. **For approval chain (multiple gates)**:
   - Manual job per gate (e.g., `approve-staging-smoke-test` → `approve-prod-deploy`)
   - Each gate can have different approvers via protected env
   - Pipeline pauses at each manual job until approved
7. **For emergency / hotfix path**:
   - Separate `hotfix` pipeline that skips some approvals
   - Carefully restricted by branch protection (only specific people can push to `hotfix/*`)
   - Post-incident review required
8. **For change-management integration**:
   - CI creates ticket via API; pipeline waits via long-poll or webhook
   - Or: CI receives webhook from external system that triggers next stage

Mark DESTRUCTIVE: bypassing protected environment approval (`force_push` to protected ref), self-approving in approval flows (against policy), deploy freeze override without compensation control.

---

Release frequency + structure: [DESCRIBE]
Current approval mechanism: [DESCRIBE]
Compliance requirements: [DESCRIBE — SOX/ISO/internal]
Target: [single env / multi-region / multi-cluster]
Goal: [design new workflow / harden existing / debug a stuck approval]

Why this prompt works

Release workflows balance speed with safety. Naive when: manual everywhere creates approval bureaucracy; no approvals creates risk. This prompt walks the patterns and lets you pick per env.

How to use it

  1. Match approval rigor to env risk — staging may be auto, prod definitely needs approval.
  2. Use protected envs for production — not just when: manual.
  3. Set deploy freezes for non-deploy hours.
  4. Define hotfix paths separately so emergencies don’t bypass everything.

Useful commands

# Protect environment via API
curl --request POST --header "PRIVATE-TOKEN: <t>" --header "Content-Type: application/json" \
    --data '{
        "name": "production",
        "deploy_access_levels": [{"access_level": 40}],
        "required_approval_count": 2
    }' \
    "https://gitlab.example.com/api/v4/projects/<id>/protected_environments"

# Add deploy freeze
curl --request POST --header "PRIVATE-TOKEN: <t>" --header "Content-Type: application/json" \
    --data '{
        "freeze_start": "0 17 * * 5",       # Friday 17:00
        "freeze_end": "0 9 * * 1",          # Monday 09:00
        "cron_timezone": "America/New_York"
    }' \
    "https://gitlab.example.com/api/v4/projects/<id>/freeze_periods"

# List recent deployments (audit trail)
curl --header "PRIVATE-TOKEN: <t>" \
    "https://gitlab.example.com/api/v4/projects/<id>/deployments?environment=production" | jq

Patterns

Multi-gate approval

stages: [build, test, deploy-staging, smoke-test-staging, approve-prod, deploy-prod]

build:
  stage: build
  script: ./build.sh

test:
  stage: test
  script: ./test.sh

deploy-staging:
  stage: deploy-staging
  script: ./deploy.sh staging
  environment: { name: staging, deployment_tier: staging }
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

smoke-test-staging:
  stage: smoke-test-staging
  needs: [deploy-staging]
  script: ./smoke-test.sh https://staging.example.com
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

approve-prod:
  stage: approve-prod
  needs: [smoke-test-staging]
  script:
    - echo "Approval received from $GITLAB_USER_LOGIN at $(date)"
  environment: { name: production-pre-approval, action: prepare }
  when: manual
  rules:
    - if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/
  allow_failure: false

deploy-prod:
  stage: deploy-prod
  needs: [approve-prod]
  script: ./deploy.sh production
  environment: { name: production, deployment_tier: production }
  rules:
    - if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/

Tag-only production releases

deploy-production:
  stage: deploy
  script: ./deploy.sh
  environment: { name: production, deployment_tier: production }
  rules:
    - if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/
      when: manual

In Settings → CI/CD → Protected environments → production → require 2 approvals.

Deploy freeze

deploy-prod:
  script: ./deploy.sh
  environment: { name: production }
  rules:
    - if: $CI_DEPLOY_FREEZE
      when: never
    - if: $CI_COMMIT_TAG
      when: manual

(GitLab sets $CI_DEPLOY_FREEZE=true during configured freeze windows.)

Hotfix path

deploy-hotfix:
  stage: deploy
  script: ./deploy.sh production
  environment: { name: production, deployment_tier: production }
  rules:
    - if: $CI_COMMIT_BRANCH =~ /^hotfix\//
      when: manual
# Pair with branch protection on hotfix/* to limit who can push

External change management (ServiceNow)

create-snow-ticket:
  stage: approve
  script:
    - |
      TICKET_ID=$(curl -u "$SNOW_USER:$SNOW_PASS" \
          -X POST -H "Content-Type: application/json" \
          -d "{\"short_description\":\"Deploy $CI_PROJECT_NAME $CI_COMMIT_TAG\",\"...}\"}" \
          "https://servicenow.example.com/api/now/table/change_request" | jq -r '.result.number')
      echo "Created ticket $TICKET_ID"
      echo "$TICKET_ID" > snow-ticket.txt
  artifacts:
    paths: [snow-ticket.txt]

wait-for-approval:
  stage: approve
  needs: [create-snow-ticket]
  script:
    - TICKET=$(cat snow-ticket.txt)
    - |
      while true; do
        STATE=$(curl -u "$SNOW_USER:$SNOW_PASS" \
            "https://servicenow.example.com/api/now/table/change_request/$TICKET" | jq -r '.result.state')
        case "$STATE" in
          implement) echo "Approved"; break ;;
          rejected) echo "Rejected"; exit 1 ;;
          *) sleep 60 ;;
        esac
      done
  timeout: 4h

Common findings this catches

  • when: manual but no protected env → anyone with developer can deploy prod.
  • Self-approval allowed → policy violation; require N > 1 approvers excluding the requester.
  • Deploy freeze defined but rule doesn’t reference $CI_DEPLOY_FREEZE → freeze ineffective.
  • No audit on manual approval clicks → who approved? Use protected env which records.
  • Tag-based release but protect: true on tags missing → anyone can create a tag.
  • External CM integration polls forever → no timeout; pipeline stalls. Set job timeout.
  • Hotfix path skips smoke tests → consider mini-test even on hotfix; tradeoff with speed.

When to escalate

  • Compliance audit failing despite controls — review the audit trail; possibly need GitLab Premium audit events.
  • Bus-factor problem (only one approver, unavailable) — back-up approvers required.
  • External system integration drift — coordinate with system owner; webhook contracts.

Related prompts

Newsletter

Get weekly AI workflows for DevOps engineers

Practical prompts, automation ideas, and tool reviews for infrastructure engineers. One email per week. No spam.