CI/CD pipeline: ssh -L tunnel for TIM API + manual Jenkins trigger
Two design pivots discovered during Phase B prerequisites: Routing: Replace static-route + NAT plan with persistent ssh -L tunnel from pve-201 to webzavod (deployment/systemd/flights-tim-tunnel.service). nginx proxies /api/ and /map/api/ to https://127.0.0.1:8443 with SNI/Host overrides so cert validation still targets the real hostname. No webzavod kernel changes (no ip_forward/MASQUERADE), no /etc/hosts pin needed. Workflow B: Drop Jenkins trigger/poll automation (operator lacks Jenkins job-configure access and user API token access). release.yml now stops after MR merge with a Telegram message containing the Jenkins job URL. release-verify.yml (new, workflow_dispatch only) runs the customer-URL e2e suite once the operator has triggered Jenkins manually and it has completed. Other: - SSR loopback port 8081 -> 3002 (8081 was taken by openwebui on pve-201) - notify-telegram.sh skips cleanly when TG secrets unset (was: hard-fail) - README + spec addendum cover the new prereqs and removed steps
This commit is contained in:
@@ -9,7 +9,7 @@
|
||||
#
|
||||
# Env:
|
||||
# GITHUB_SHA (required for swap)
|
||||
# FLIGHTS_WEB_PORT (default 8081 — host port that nginx proxies to)
|
||||
# FLIGHTS_WEB_PORT (default 3002 — host port that nginx proxies to)
|
||||
# IMAGE_NAME (default flights-web — set this to point at a registry later)
|
||||
set -euo pipefail
|
||||
|
||||
@@ -20,7 +20,7 @@ if [ "${1:-}" = "--dry-run" ]; then
|
||||
fi
|
||||
|
||||
CMD="${1:-}"
|
||||
PORT="${FLIGHTS_WEB_PORT:-8081}"
|
||||
PORT="${FLIGHTS_WEB_PORT:-3002}"
|
||||
IMAGE="${IMAGE_NAME:-flights-web}"
|
||||
|
||||
run() {
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
#!/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 — see ${BUILD_URL}console" >&2
|
||||
exit 1
|
||||
fi
|
||||
sleep "$POLL_INTERVAL"
|
||||
done
|
||||
@@ -28,8 +28,10 @@ esac
|
||||
[ -n "$STAGE" ] || { echo "usage: $0 [--dry-run] <start|ok|fail> <stage> [<extra-context>]" >&2; exit 2; }
|
||||
|
||||
if [ "$DRY_RUN" -eq 0 ]; then
|
||||
: "${TELEGRAM_BOT_TOKEN:?TELEGRAM_BOT_TOKEN required}"
|
||||
: "${TELEGRAM_CHAT_ID:?TELEGRAM_CHAT_ID required}"
|
||||
if [ -z "${TELEGRAM_BOT_TOKEN:-}" ] || [ -z "${TELEGRAM_CHAT_ID:-}" ]; then
|
||||
echo "notify-telegram: TELEGRAM_BOT_TOKEN/TELEGRAM_CHAT_ID unset — skipping" >&2
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
REPO="${GITHUB_REPOSITORY:-unknown/repo}"
|
||||
|
||||
Reference in New Issue
Block a user