diff --git a/conditional-variables/action.yaml b/conditional-variables/action.yaml new file mode 100644 index 0000000..a1898ea --- /dev/null +++ b/conditional-variables/action.yaml @@ -0,0 +1,45 @@ +name: Conditional Variables + +description: | + Evaluates a list of conditions and exposes variables as outputs. + Conditions are evaluated in order. All matching conditions are applied. + If multiple conditions set the same variable, the last matching value wins. + +inputs: + context: + description: | + Context variables available during condition evaluation. + + conditions: + description: | + List of conditions to evaluate. + + Example: + + - when: + branch: + - 9.0-alpine-24 + - 9.0-alpine-23 + set: + DOTNET_VERSION: 9.0 + + required: true + + fail-on-no-match: + description: | + Fail the action when no condition matches. + required: false + default: "false" + +runs: + using: composite + + steps: + - name: Evaluate conditions + shell: bash + env: + CONTEXT: ${{ inputs.context }} + CONDITIONS: ${{ inputs.conditions }} + FAIL_ON_NO_MATCH: ${{ inputs.fail-on-no-match }} + run: | + "${{ github.action_path }}/evaluate.sh" \ No newline at end of file diff --git a/conditional-variables/evaluate.sh b/conditional-variables/evaluate.sh new file mode 100644 index 0000000..894eb2d --- /dev/null +++ b/conditional-variables/evaluate.sh @@ -0,0 +1,141 @@ +trim() { + local var="$*" + var="${var#"${var%%[![:space:]]*}"}" + var="${var%"${var##*[![:space:]]}"}" + printf '%s' "$var" +} + +declare -A context + +# +# Load context +# +while IFS='=' read -r key value; do + + key="$(trim "${key:-}")" + value="$(trim "${value:-}")" + + [[ -z "$key" ]] && continue + [[ "$key" =~ ^# ]] && continue + + context["$key"]="$value" + +done <<< "$CONTEXT" + +matched=false + +# +# Evaluate conditions (OR) +# +while IFS= read -r condition; do + + condition="$(trim "${condition:-}")" + + [[ -z "$condition" ]] && continue + [[ "$condition" =~ ^# ]] && continue + + read -r variable operator expected <<< "$condition" + + # + # Validate condition format + # + if [[ -z "${variable:-}" || -z "${operator:-}" || -z "${expected:-}" ]]; then + echo "Invalid condition: $condition" + exit 1 + fi + + if [[ ! -v context["$variable"] ]]; then + echo "Unknown context variable '$variable'" + exit 1 + fi + + actual="${context[$variable]}" + + case "$operator" in + + "==") + [[ "$actual" == "$expected" ]] && matched=true + ;; + + "!=") + [[ "$actual" != "$expected" ]] && matched=true + ;; + + ">"|">="|"<"|"<=") + + # + # Numeric comparisons only + # + if [[ ! "$actual" =~ ^-?[0-9]+$ ]]; then + echo "Numeric operator '$operator' requires integer value. Actual: '$actual'" + exit 1 + fi + + if [[ ! "$expected" =~ ^-?[0-9]+$ ]]; then + echo "Numeric operator '$operator' requires integer comparison value. Expected: '$expected'" + exit 1 + fi + + case "$operator" in + ">") + (( actual > expected )) && matched=true + ;; + ">=") + (( actual >= expected )) && matched=true + ;; + "<") + (( actual < expected )) && matched=true + ;; + "<=") + (( actual <= expected )) && matched=true + ;; + esac + ;; + + *) + echo "Unsupported operator: $operator" + exit 1 + ;; + + esac + + # + # OR logic: + # stop at first match + # + [[ "$matched" == true ]] && break + +done <<< "$CONDITIONS" + +# +# Set outputs +# +if [[ "$matched" == true ]]; then + + while IFS= read -r output; do + + output="$(trim "${output:-}")" + + [[ -z "$output" ]] && continue + [[ "$output" =~ ^# ]] && continue + + # + # Validate output syntax + # + if [[ ! "$output" =~ ^[A-Za-z_][A-Za-z0-9_]*= ]]; then + echo "Invalid variable definition: $output" + exit 1 + fi + + echo "$output" >> "$GITHUB_OUTPUT" + + done <<< "$VARIABLES" + +fi + +if [[ "$matched" == false && "$FAIL_ON_NO_MATCH" == "true" ]]; then + echo "No conditions matched" + exit 1 +fi + +echo "matched=$matched" >> "$GITHUB_OUTPUT" \ No newline at end of file