#!/bin/bash # fsm.sh — лёгкий конечный автомат для GitHub Actions (composite) set -euo pipefail DEBUG="${INPUT_DEBUG:-false}" [[ "$DEBUG" == "true" ]] && set -x log() { echo "::notice::$1"; } debug() { [[ "$DEBUG" == "true" ]] && echo "::debug::$1"; } error() { echo "::error::$1"; exit 1; } # Установка jq, если его нет (работает на всех официальных runner-ах) if ! command -v jq &>/dev/null; then if [[ -f /etc/alpine-release ]]; then apk add --no-cache jq elif [[ -f /etc/debian_version ]]; then sudo apt-get update && sudo apt-get install -y jq elif [[ "$RUNNER_OS" == "macOS" ]]; then brew install jq elif [[ "$RUNNER_OS" == "Windows" ]]; then choco install jq -y else error "Не удалось установить jq на $RUNNER_OS" fi fi STATES_JSON="$INPUT_STATES" CURRENT="$INPUT_INITIAL" EVENT="$INPUT_EVENT" CONTEXT_JSON="${INPUT_CONTEXT:-{}}" debug "States: $STATES_JSON" debug "From: $CURRENT → Event: $EVENT" debug "Context: $CONTEXT_JSON" # Ищем подходящий переход (поддержка wildcard события *) TRANSITION=$(echo "$STATES_JSON" | jq -r --arg from "$CURRENT" --arg event "$EVENT" ' .transitions // [] | .[] | select(.from == $from and (.event == $event or .event == "*")) | { to: .to, action: (.action // "") } | @json ' | head -n1) [[ -z "$TRANSITION" || "$TRANSITION" == "null" ]] && \ error "Нет перехода из состояния '$CURRENT' по событию '$EVENT'" NEXT_STATE=$(echo "$TRANSITION" | jq -r '.to') ACTION=$(echo "$TRANSITION" | jq -r '.action') log "Переход: $CURRENT --[$EVENT]--> $NEXT_STATE" EXECUTED=() if [[ -n "$ACTION" && "$ACTION" != "null" ]]; then log "Выполняется действие: $ACTION" # Подстановка переменных вида ${{ var }} из контекста + env ACTION_EXP=$(echo "$ACTION" | CONTEXT="$CONTEXT_JSON" jq -r ' reduce paths(scalars) as $p (.; setpath($p; getpath($p) | if type == "string" then gsub("\\$\\{\\{[ \\t]*([a-zA-Z_][a-zA-Z0-9_]*)[ \\t]*\\}\\}" ; (capture("\\$\\{\\{[ \\t]*(?[a-zA-Z_][a-zA-Z0-9_]*)[ \\t]*\\}\\}") |.v) as $var | ($ENV.CONTEXT | fromjson | .[$var] // env[$var] // "") ) else . end ) ) ') debug "Расширенная команда: $ACTION_EXP" eval "$ACTION_EXP" EXECUTED+=("$ACTION") fi # Выводим outputs echo "next_state=$NEXT_STATE" >> "$GITHUB_OUTPUT" echo "executed_actions=$(jq -c --null-input '$EXECUTED')" >> "$GITHUB_OUTPUT" log "Новое состояние: $NEXT_STATE" [[ ${#EXECUTED[@]} -gt 0 ]] && log "Выполнено действий: ${#EXECUTED[@]}"