# knooing Change-Anbindung für GitLab CI (A1-34)
#
# Einbinden:
#   include:
#     - local: gitlab-ci-template.yml   # Datei im eigenen Repository, oder
#     - remote: https://www.knooing.com/downloads/pipeline/gitlab-ci-template.yml
#
# Pflicht-Variablen (Projekt > Einstellungen > CI/CD > Variablen):
#   KNOOING_API_KEY     API-Schlüssel mit Scope changes:write. Als "Masked" und "Protected"
#                       anlegen, nie in dieser Datei oder in .gitlab-ci.yml eintragen.
#   KNOOING_SERVICE_ID  ID des Services, für den der Change angelegt wird.
#
# Ablauf: knooing-change-open läuft in der Stufe .pre (vor allen anderen Jobs), die Statusjobs
# laufen in der Stufe .post (nach allen anderen Jobs, also nach Ihrem Deploy). Der Statusjob
# knooing-change-status läuft nur, wenn alle früheren Jobs erfolgreich waren, und meldet
# "successful". knooing-change-status-failed läuft nur, wenn mindestens ein früherer Job
# fehlgeschlagen ist, und meldet "failed". Alle drei Jobs laufen nur auf dem Standard-Branch
# und auf geschützten Branches/Tags. Feinsteuerung (needs auf den eigenen Deploy-Job):
# der Anleitung auf knooing.com/de/docs (Change-Anbindung für Pipelines).
#
# Optional: KNOOING_API_URL, KNOOING_CHANGE_TITLE, KNOOING_CHANGE_DESCRIPTION, KNOOING_IMPACT,
#   KNOOING_URGENCY, KNOOING_CHANGE_CLASS, KNOOING_ROLLBACK_PLAN, KNOOING_EXTERNAL_KEY,
#   KNOOING_OUTCOME, KNOOING_NOTE (Beschreibung: Anleitung auf knooing.com/de/docs).

variables:
  KNOOING_API_URL: "https://api.knooing.info"
  KNOOING_CHANGE_TITLE: "Deployment $CI_PROJECT_NAME $CI_COMMIT_SHORT_SHA"
  KNOOING_CHANGE_DESCRIPTION: "Automatisches Deployment aus Pipeline $CI_PIPELINE_ID ($CI_PIPELINE_URL)"
  KNOOING_IMPACT: "2"
  KNOOING_URGENCY: "2"
  KNOOING_CHANGE_CLASS: "standard"
  KNOOING_ROLLBACK_PLAN: ""
  # Wiederholte Läufe derselben Pipeline legen keinen zweiten Change an.
  KNOOING_EXTERNAL_KEY: "gitlab-$CI_PROJECT_ID-$CI_PIPELINE_ID"

.knooing-change-base:
  image: alpine:3.20
  before_script:
    - apk add --no-cache curl jq >/dev/null

knooing-change-open:
  extends: .knooing-change-base
  stage: .pre
  rules:
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_REF_PROTECTED == "true"'
  script:
    # --- knooing-open:begin
    - |
      set -eu
      (set -o pipefail) 2>/dev/null && set -o pipefail || true
      resp="${KNOOING_RESPONSE_FILE:-knooing-response.json}"
      : "${KNOOING_API_KEY:?KNOOING_API_KEY fehlt (maskierte CI-Variable)}"
      : "${KNOOING_SERVICE_ID:?KNOOING_SERVICE_ID fehlt}"
      body=$(jq -n \
        --arg serviceId "$KNOOING_SERVICE_ID" \
        --arg title "$KNOOING_CHANGE_TITLE" \
        --arg description "$KNOOING_CHANGE_DESCRIPTION" \
        --argjson impact "$KNOOING_IMPACT" \
        --argjson urgency "$KNOOING_URGENCY" \
        --arg changeClass "$KNOOING_CHANGE_CLASS" \
        --arg rollbackPlan "${KNOOING_ROLLBACK_PLAN:-}" \
        --arg externalKey "${KNOOING_EXTERNAL_KEY:-}" \
        '{serviceId: $serviceId, title: $title, description: $description, impact: $impact, urgency: $urgency, changeClass: $changeClass}
         + (if $rollbackPlan == "" then {} else {rollbackPlan: $rollbackPlan} end)
         + (if $externalKey == "" then {} else {externalKey: $externalKey} end)')
      http=$(curl -sS --max-time 30 -o "$resp" -w '%{http_code}' \
        -X POST "${KNOOING_API_URL%/}/api/pipeline/changes" \
        -H "X-API-Key: $KNOOING_API_KEY" -H 'Content-Type: application/json' \
        --data "$body")
      case "$http" in
        201)
          id=$(jq -r '.changeRecord.id' "$resp")
          echo "knooing: Change angelegt (Vorgang $(jq -r '.ticket.number' "$resp"), Change-Datensatz $id)."
          printf 'KNOOING_CHANGE_ID=%s\n' "$id" > "${KNOOING_OUTPUT_FILE:-knooing-change.env}"
          ;;
        200)
          id=$(jq -r '.changeRecordId // empty' "$resp")
          echo "knooing: Change mit diesem externalKey existiert bereits (Vorgang $(jq -r '.ticketId' "$resp"), Change-Datensatz ${id:-unbekannt}), es wurde nichts angelegt."
          printf 'KNOOING_CHANGE_ID=%s\n' "$id" > "${KNOOING_OUTPUT_FILE:-knooing-change.env}"
          ;;
        *)
          echo "knooing: Anlage fehlgeschlagen (HTTP $http): $(jq -r '.error // "unbekannt"' "$resp" 2>/dev/null) [$(jq -r '.code // "-"' "$resp" 2>/dev/null)]" >&2
          exit 1
          ;;
      esac
    # --- knooing-open:end
  artifacts:
    reports:
      dotenv: knooing-change.env
    expire_in: 1 day

knooing-change-status:
  extends: .knooing-change-base
  # .post läuft nach allen anderen Stufen. Ohne `needs` wartet der Job auf alle früheren Jobs und
  # erhält die Artefakte (dotenv mit der Change-ID) aus allen früheren Stufen.
  stage: .post
  rules:
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_REF_PROTECTED == "true"'
  variables:
    KNOOING_OUTCOME: "successful"
    KNOOING_NOTE: "Pipeline $CI_PIPELINE_ID: $CI_PIPELINE_URL"
  script:
    # --- knooing-status:begin
    - |
      set -eu
      (set -o pipefail) 2>/dev/null && set -o pipefail || true
      resp="${KNOOING_RESPONSE_FILE:-knooing-response.json}"
      : "${KNOOING_API_KEY:?KNOOING_API_KEY fehlt (maskierte CI-Variable)}"
      if [ -z "${KNOOING_CHANGE_ID:-}" ]; then
        echo "knooing: keine Change-ID vorhanden (Change wurde nicht neu angelegt), Status wird nicht gemeldet."
        exit 0
      fi
      body=$(jq -n --arg outcome "$KNOOING_OUTCOME" --arg note "${KNOOING_NOTE:-}" \
        '{outcome: $outcome} + (if $note == "" then {} else {note: $note} end)')
      http=$(curl -sS --max-time 30 -o "$resp" -w '%{http_code}' \
        -X POST "${KNOOING_API_URL%/}/api/pipeline/changes/$KNOOING_CHANGE_ID/status" \
        -H "X-API-Key: $KNOOING_API_KEY" -H 'Content-Type: application/json' \
        --data "$body")
      if [ "$http" != "200" ]; then
        echo "knooing: Statusmeldung fehlgeschlagen (HTTP $http): $(jq -r '.error // "unbekannt"' "$resp" 2>/dev/null) [$(jq -r '.code // "-"' "$resp" 2>/dev/null)]" >&2
        exit 1
      fi
      echo "knooing: Ergebnis $KNOOING_OUTCOME für Change $KNOOING_CHANGE_ID gemeldet."
    # --- knooing-status:end

# Meldet einen fehlgeschlagenen Rollout. `when: on_failure` gilt laut GitLab-Dokumentation, wenn
# mindestens ein Job einer früheren Stufe fehlgeschlagen ist. Die Regel steht in `rules`, weil `when`
# dort je Regel gesetzt wird. Ist schon der Open-Job fehlgeschlagen, gibt es keine Change-ID, und der Job
# beendet sich ohne Meldung.
knooing-change-status-failed:
  extends: knooing-change-status
  rules:
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_REF_PROTECTED == "true"'
      when: on_failure
  variables:
    KNOOING_OUTCOME: "failed"
    KNOOING_NOTE: "Pipeline $CI_PIPELINE_ID fehlgeschlagen: $CI_PIPELINE_URL"
