Загрузка данных
sudo tee /usr/local/bin/watch-gitlab-builds >/dev/null <<'EOF'
#!/usr/bin/env bash
set -uo pipefail
INTERVAL="${1:-3}"
LOG_LINES="${LOG_LINES:-15}"
if ! [[ "${INTERVAL}" =~ ^[0-9]+([.][0-9]+)?$ ]]; then
echo "Usage: $0 [refresh_interval_seconds]"
exit 1
fi
# Использовать docker без sudo, если текущий пользователь имеет доступ.
if docker info >/dev/null 2>&1; then
DOCKER=(docker)
else
sudo -v || exit 1
DOCKER=(sudo docker)
fi
cleanup()
{
printf '\033[?25h'
}
trap cleanup EXIT INT TERM
# Скрыть курсор.
printf '\033[?25l'
while true; do
mapfile -t CONTAINERS < <(
"${DOCKER[@]}" ps \
--format '{{.ID}}|{{.Names}}|{{.Status}}' \
2>/dev/null |
awk -F '|' '$2 ~ /^runner-.*-build$/'
)
# Очистить экран и поставить курсор в начало.
printf '\033[2J\033[H'
printf 'GitLab Runner build monitor\n'
printf 'Time: %s\n' "$(date '+%Y-%m-%d %H:%M:%S %Z')"
printf 'Refresh: %s seconds\n' "${INTERVAL}"
printf 'Active build containers: %d\n' "${#CONTAINERS[@]}"
printf '%s\n' '================================================================================'
if [ "${#CONTAINERS[@]}" -eq 0 ]; then
echo
echo "Нет активных сборок GitLab Runner."
echo "Ожидание новой job..."
sleep "${INTERVAL}"
continue
fi
for CONTAINER_INFO in "${CONTAINERS[@]}"; do
IFS='|' read -r CID CONTAINER_NAME CONTAINER_STATUS \
<<< "${CONTAINER_INFO}"
# Контейнер мог завершиться между docker ps и docker inspect.
ENVIRONMENT="$(
"${DOCKER[@]}" inspect \
--format '{{range .Config.Env}}{{println .}}{{end}}' \
"${CID}" 2>/dev/null || true
)"
if [ -z "${ENVIRONMENT}" ]; then
continue
fi
JOB_NAME="$(
sed -n 's/^CI_JOB_NAME=//p' <<< "${ENVIRONMENT}" |
head -n1
)"
JOB_ID="$(
sed -n 's/^CI_JOB_ID=//p' <<< "${ENVIRONMENT}" |
head -n1
)"
PIPELINE_ID="$(
sed -n 's/^CI_PIPELINE_ID=//p' <<< "${ENVIRONMENT}" |
head -n1
)"
PROJECT_DIR="$(
sed -n 's/^CI_PROJECT_DIR=//p' <<< "${ENVIRONMENT}" |
head -n1
)"
OUTPUT_DIR="$(
sed -n 's/^OUTPUT_DIR=//p' <<< "${ENVIRONMENT}" |
head -n1
)"
JOB_NAME="${JOB_NAME:-unknown}"
JOB_ID="${JOB_ID:-unknown}"
PIPELINE_ID="${PIPELINE_ID:-unknown}"
LOG_FILE=""
if [ -n "${OUTPUT_DIR}" ]; then
if [[ "${OUTPUT_DIR}" = /* ]]; then
LOG_FILE="${OUTPUT_DIR}/build.log"
elif [ -n "${PROJECT_DIR}" ]; then
LOG_FILE="${PROJECT_DIR}/${OUTPUT_DIR}/build.log"
fi
fi
# Резервный поиск, если OUTPUT_DIR отсутствует в окружении.
if [ -z "${LOG_FILE}" ]; then
LOG_FILE="$(
"${DOCKER[@]}" exec "${CID}" sh -lc '
base="${CI_PROJECT_DIR:-/builds}"
find "${base}/output" \
-maxdepth 3 \
-type f \
-name build.log \
-printf "%T@ %p\n" 2>/dev/null |
sort -nr |
head -n1 |
cut -d" " -f2-
' 2>/dev/null || true
)"
fi
STATS="$(
"${DOCKER[@]}" stats \
--no-stream \
--format \
'CPU={{.CPUPerc}} MEM={{.MemUsage}} NET={{.NetIO}} BLOCK={{.BlockIO}} PIDS={{.PIDs}}' \
"${CID}" 2>/dev/null || true
)"
printf '\n'
printf 'JOB: %s\n' "${JOB_NAME}"
printf 'Job ID: %s Pipeline ID: %s\n' \
"${JOB_ID}" "${PIPELINE_ID}"
printf 'CID: %s\n' "${CID}"
printf 'Container: %s\n' "${CONTAINER_NAME}"
printf 'Status: %s\n' "${CONTAINER_STATUS}"
printf 'Resources: %s\n' "${STATS:-unavailable}"
printf 'Output: %s\n' "${OUTPUT_DIR:-not set}"
printf 'Log: %s\n' "${LOG_FILE:-not found}"
printf '%s\n' '--------------------------------------------------------------------------------'
if [ -n "${LOG_FILE}" ]; then
CURRENT_STAGE="$(
"${DOCKER[@]}" exec "${CID}" sh -lc '
log="$1"
if [ -f "${log}" ]; then
grep "^>>>" "${log}" |
tail -n1
fi
' sh "${LOG_FILE}" 2>/dev/null || true
)"
if [ -n "${CURRENT_STAGE}" ]; then
printf 'CURRENT BUILROOT STAGE:\n%s\n' "${CURRENT_STAGE}"
else
echo "CURRENT BUILDROOT STAGE: ещё не определён"
fi
printf '\nLAST %s LOG LINES:\n' "${LOG_LINES}"
"${DOCKER[@]}" exec "${CID}" sh -lc '
log="$1"
lines="$2"
if [ -f "${log}" ]; then
tail -n "${lines}" "${log}"
else
echo "build.log ещё не создан"
fi
' sh "${LOG_FILE}" "${LOG_LINES}" 2>/dev/null || true
else
echo "build.log пока не найден."
fi
printf '\nTOP PROCESSES:\n'
"${DOCKER[@]}" exec "${CID}" sh -lc '
ps -eo pid,etime,%cpu,%mem,comm \
--sort=-%cpu 2>/dev/null |
head -n7
' 2>/dev/null || true
printf '%s\n' '================================================================================'
done
sleep "${INTERVAL}"
done
EOF
sudo chmod +x /usr/local/bin/watch-gitlab-builds