85 lines
2.8 KiB
Bash
85 lines
2.8 KiB
Bash
#!/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]*(?<v>[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[@]}" |