ci: jenkins-trigger-and-wait.sh — fire job + poll for SUCCESS

This commit is contained in:
2026-04-25 02:45:24 +03:00
parent dd8b933ec3
commit 0cd8d0c102
2 changed files with 155 additions and 0 deletions
+124
View File
@@ -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 <fixture.json> # 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 <fixture.json>" >&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
+31
View File
@@ -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"