From 0cd8d0c1026783f095fa189f4ddba74880b70845 Mon Sep 17 00:00:00 2001 From: gnezim Date: Sat, 25 Apr 2026 02:45:24 +0300 Subject: [PATCH] =?UTF-8?q?ci:=20jenkins-trigger-and-wait.sh=20=E2=80=94?= =?UTF-8?q?=20fire=20job=20+=20poll=20for=20SUCCESS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/ci/jenkins-trigger-and-wait.sh | 124 +++++++++++++++++++++++++ tests/ci/test-jenkins-trigger.sh | 31 +++++++ 2 files changed, 155 insertions(+) create mode 100755 scripts/ci/jenkins-trigger-and-wait.sh create mode 100755 tests/ci/test-jenkins-trigger.sh diff --git a/scripts/ci/jenkins-trigger-and-wait.sh b/scripts/ci/jenkins-trigger-and-wait.sh new file mode 100755 index 00000000..18aa32a1 --- /dev/null +++ b/scripts/ci/jenkins-trigger-and-wait.sh @@ -0,0 +1,124 @@ +#!/usr/bin/env bash +# jenkins-trigger-and-wait.sh — fire a Jenkins job and wait for completion. +# +# Usage: +# jenkins-trigger-and-wait.sh # real mode (env-driven) +# jenkins-trigger-and-wait.sh --mock-mode # for tests +# +# Env (real mode): +# JENKINS_BASE_URL e.g. http://jenkins.yc.devwebzavod.ru:8080 +# JENKINS_JOB_PATH e.g. /job/Aeroflot2/job/Flights-Front-Dev +# JENKINS_USER, JENKINS_API_TOKEN +# JENKINS_TRIGGER_TOKEN +# JENKINS_TIMEOUT seconds (default 1800) +# JENKINS_POLL_INTERVAL seconds (default 10) +set -euo pipefail + +MODE=real +FIXTURE="" +if [ "${1:-}" = "--mock-mode" ]; then + MODE=mock + FIXTURE="${2:-}" + [ -n "$FIXTURE" ] || { echo "usage: $0 --mock-mode " >&2; exit 2; } + command -v jq >/dev/null 2>&1 || { echo "fatal: jq required for --mock-mode" >&2; exit 2; } +fi + +POLL_INTERVAL="${JENKINS_POLL_INTERVAL:-10}" +TIMEOUT="${JENKINS_TIMEOUT:-1800}" + +if [ "$MODE" = real ]; then + : "${JENKINS_BASE_URL:?required}" + : "${JENKINS_JOB_PATH:?required}" + : "${JENKINS_USER:?required}" + : "${JENKINS_API_TOKEN:?required}" + : "${JENKINS_TRIGGER_TOKEN:?required}" +fi + +# ── Mock mode: walk fixture deterministically ───────────────────────────────── +if [ "$MODE" = mock ]; then + QUEUE_URL=$(jq -r '.trigger_response.headers.Location' "$FIXTURE") + echo "triggered (mock): queue=$QUEUE_URL" + + # Walk queue polls until we get an executable. + count=$(jq '.queue_polls | length' "$FIXTURE") + BUILD_URL="" + for i in $(seq 0 $((count - 1))); do + body=$(jq -c ".queue_polls[$i].body" "$FIXTURE") + exe_url=$(printf '%s' "$body" | jq -r '.executable.url // empty') + if [ -n "$exe_url" ]; then + BUILD_URL="$exe_url" + break + fi + echo "queue poll $((i + 1)): not yet" + done + [ -n "${BUILD_URL:-}" ] || { echo "fatal: queue never produced executable" >&2; exit 1; } + echo "build url (mock): $BUILD_URL" + + # Walk build polls until result != null. + count=$(jq '.build_polls | length' "$FIXTURE") + for i in $(seq 0 $((count - 1))); do + body=$(jq -c ".build_polls[$i].body" "$FIXTURE") + result=$(printf '%s' "$body" | jq -r '.result // empty') + number=$(printf '%s' "$body" | jq -r '.number') + if [ -n "$result" ]; then + if [ "$result" = "SUCCESS" ]; then + echo "build #${number} SUCCESS" + exit 0 + else + echo "build #${number} ${result}" >&2 + exit 1 + fi + fi + echo "build poll $((i + 1)): building" + done + echo "fatal: build never completed within fixture" >&2 + exit 1 +fi + +# ── Real mode ───────────────────────────────────────────────────────────────── +TRIGGER_URL="${JENKINS_BASE_URL}${JENKINS_JOB_PATH}/build?token=${JENKINS_TRIGGER_TOKEN}" +echo "triggering: $TRIGGER_URL" + +# -D - dumps headers; -o /dev/null discards body. We need the Location header. +HEADERS=$(curl -fsS -X POST -u "${JENKINS_USER}:${JENKINS_API_TOKEN}" -D - -o /dev/null "$TRIGGER_URL") +QUEUE_URL=$(printf '%s' "$HEADERS" | grep -i '^Location:' | head -1 | sed 's/^[Ll]ocation:[[:space:]]*//' | tr -d '\r\n') +[ -n "$QUEUE_URL" ] || { echo "fatal: no Location header from Jenkins" >&2; exit 1; } +echo "queue: $QUEUE_URL" + +# Poll queue for executable.url. START covers both queue + build phases. +START=$(date +%s) +BUILD_URL="" +while [ -z "$BUILD_URL" ]; do + resp=$(curl -fsS -u "${JENKINS_USER}:${JENKINS_API_TOKEN}" "${QUEUE_URL}api/json") + BUILD_URL=$(printf '%s' "$resp" | jq -r '.executable.url // empty') + [ -n "$BUILD_URL" ] && break + now=$(date +%s) + if [ $((now - START)) -ge "$TIMEOUT" ]; then + echo "fatal: queue timeout after ${TIMEOUT}s" >&2 + exit 1 + fi + sleep "$POLL_INTERVAL" +done +echo "build: $BUILD_URL" + +# Poll build for result. Timeout window is shared with queue phase (START not reset). +while :; do + resp=$(curl -fsS -u "${JENKINS_USER}:${JENKINS_API_TOKEN}" "${BUILD_URL}api/json") + result=$(printf '%s' "$resp" | jq -r '.result // empty') + number=$(printf '%s' "$resp" | jq -r '.number') + if [ -n "$result" ]; then + if [ "$result" = "SUCCESS" ]; then + echo "build #${number} SUCCESS" + exit 0 + else + echo "build #${number} ${result} — see ${BUILD_URL}console" >&2 + exit 1 + fi + fi + now=$(date +%s) + if [ $((now - START)) -ge "$TIMEOUT" ]; then + echo "fatal: build timeout after ${TIMEOUT}s" >&2 + exit 1 + fi + sleep "$POLL_INTERVAL" +done diff --git a/tests/ci/test-jenkins-trigger.sh b/tests/ci/test-jenkins-trigger.sh new file mode 100755 index 00000000..b4c1780c --- /dev/null +++ b/tests/ci/test-jenkins-trigger.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +SCRIPT="$ROOT/scripts/ci/jenkins-trigger-and-wait.sh" +[ -x "$SCRIPT" ] || { echo "FAIL: $SCRIPT not executable"; exit 1; } + +# Mock-mode tests need jq — bail with a useful message if unavailable. +command -v jq >/dev/null 2>&1 || { echo "SKIP: jq not installed"; exit 0; } + +# --- success path --- +if ! "$SCRIPT" --mock-mode "$ROOT/tests/ci/fixtures/jenkins-success-flow.json" 2>&1 | tee /tmp/jenkins-test.log; then + echo "FAIL: success fixture should exit 0" + exit 1 +fi +grep -q "build #42 SUCCESS" /tmp/jenkins-test.log || { echo "FAIL: expected 'build #42 SUCCESS'"; exit 1; } + +# --- failure path --- +if "$SCRIPT" --mock-mode "$ROOT/tests/ci/fixtures/jenkins-failure-flow.json" 2>&1 | tee /tmp/jenkins-test.log; then + echo "FAIL: failure fixture should exit non-zero" + exit 1 +fi +grep -q "FAILURE" /tmp/jenkins-test.log || { echo "FAIL: expected 'FAILURE' in output"; exit 1; } + +# --- bad usage --- +if "$SCRIPT" 2>/dev/null; then + echo "FAIL: expected usage error" + exit 1 +fi + +echo "PASS: jenkins-trigger-and-wait.sh"