From 7494563885816c83df04f5b3539e934bf29e6ca7 Mon Sep 17 00:00:00 2001 From: alexbur Date: Fri, 28 Nov 2025 11:30:28 +0500 Subject: [PATCH] Start --- action.yml | 47 ++++++++++++++++++++++++++++++ fsm.sh | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 action.yml create mode 100644 fsm.sh diff --git a/action.yml b/action.yml new file mode 100644 index 0000000..2db8e9b --- /dev/null +++ b/action.yml @@ -0,0 +1,47 @@ +name: 'Finite State Machine (FSM) Action' +description: 'Лёгкий конечный автомат на чистом Bash без Docker' +author: '4ma.su community' +inputs: + states: + description: 'JSON с переходами (обязательно)' + required: true + initial: + description: 'Начальное состояние' + required: true + event: + description: 'Событие для обработки' + required: true + context: + description: 'JSON-контекст для подстановки в действия ${{ var }}' + required: false + default: '{}' + debug: + description: 'Включить отладку' + required: false + default: 'false' + +outputs: + next_state: + description: 'Следующее состояние' + value: ${{ steps.fsm.outputs.next_state }} + executed_actions: + description: 'Массив выполненных действий' + value: ${{ steps.fsm.outputs.executed_actions }} + +runs: + using: 'composite' + steps: + - id: fsm + name: Run FSM + shell: bash + env: + INPUT_STATES: ${{ inputs.states }} + INPUT_INITIAL: ${{ inputs.initial }} + INPUT_EVENT: ${{ inputs.event }} + INPUT_CONTEXT: ${{ inputs.context }} + INPUT_DEBUG: ${{ inputs.debug }} + run: ${{ github.action_path }}/fsm.sh + +branding: + icon: 'activity' + color: 'blue' \ No newline at end of file diff --git a/fsm.sh b/fsm.sh new file mode 100644 index 0000000..10c5bc3 --- /dev/null +++ b/fsm.sh @@ -0,0 +1,85 @@ +#!/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[@]}" \ No newline at end of file