#!/usr/bin/env bash set -euo pipefail fail() { echo "::error::$*" >&2 exit 1 } require_command() { command -v "$1" >/dev/null 2>&1 || fail "Required command '$1' was not found on the runner." } require_environment() { [[ -n "${!1:-}" ]] || fail "Required environment variable '$1' is not set." } validate_common_inputs() { [[ "$GODOT_VERSION" =~ ^4(\.[0-9]+){1,2}(-[0-9A-Za-z][0-9A-Za-z._-]*)?$ ]] || fail "godot-version must be a Godot 4 release tag such as 4.7.2-stable." [[ "$GODOT_HOST_OS" == "linux" ]] || fail "host-os must be 'linux' when this action runs on Linux." [[ "$GODOT_DOTNET" == "true" || "$GODOT_DOTNET" == "false" ]] || fail "dotnet must be 'true' or 'false'." [[ "$GODOT_EXPORT_MODE" == "release" || "$GODOT_EXPORT_MODE" == "debug" ]] || fail "export-mode must be 'release' or 'debug'." [[ -n "$GODOT_EXPORT_PRESET" ]] || fail "export-preset must not be empty." [[ -n "$GODOT_EXPORT_FILE_NAME" && "$GODOT_EXPORT_FILE_NAME" != "." && "$GODOT_EXPORT_FILE_NAME" != ".." ]] || fail "export-file-name must be a filename." [[ "$GODOT_EXPORT_FILE_NAME" != */* && "$GODOT_EXPORT_FILE_NAME" != *\\* ]] || fail "export-file-name must not contain a path separator." } actual_architecture() { case "$(uname -m)" in x86_64|amd64) echo x86_64 ;; i?86) echo x86_32 ;; aarch64|arm64) echo arm64 ;; armv7l|armv6l) echo arm32 ;; *) fail "Unsupported Linux runner architecture: $(uname -m)." ;; esac } download_and_verify() { local asset="$1" local destination="$2" local sum_file="$3" local expected local actual curl --fail --location --retry 3 --silent --show-error "$BASE_URL/$asset" --output "$destination" expected="$(awk -v asset="$asset" '$2 == asset || $2 == "*" asset { print $1; exit }' "$sum_file")" [[ -n "$expected" ]] || fail "Release checksum manifest does not contain $asset." actual="$(sha512sum "$destination" | awk '{ print $1 }')" [[ "$actual" == "$expected" ]] || fail "SHA-512 verification failed for $asset." } find_editor() { find "$EDITOR_DIR" -maxdepth 3 -type f -name 'Godot*' -print -quit } install_godot() { local sum_file="$CACHE_DIR/SHA512-SUMS.txt" local editor_archive="$CACHE_DIR/$EDITOR_ASSET" local templates_archive="$CACHE_DIR/$TEMPLATES_ASSET" rm -rf "$CACHE_DIR" mkdir -p "$EDITOR_DIR" "$TEMPLATE_DIR" curl --fail --location --retry 3 --silent --show-error "$BASE_URL/SHA512-SUMS.txt" --output "$sum_file" download_and_verify "$EDITOR_ASSET" "$editor_archive" "$sum_file" download_and_verify "$TEMPLATES_ASSET" "$templates_archive" "$sum_file" unzip -q "$editor_archive" -d "$EDITOR_DIR" unzip -q "$templates_archive" -d "$TEMPLATE_DIR" EDITOR_EXECUTABLE="$(find_editor)" [[ -n "$EDITOR_EXECUTABLE" ]] || fail "The Godot editor archive did not contain an executable." [[ -f "$TEMPLATE_DIR/version.txt" ]] || fail "The export-template archive did not contain version.txt." printf 'release=%s\neditor=%s\ntemplates=%s\n' "$GODOT_VERSION" "$EDITOR_ASSET" "$TEMPLATES_ASSET" > "$CACHE_DIR/.complete" } require_environment GODOT_VERSION require_environment GODOT_HOST_OS require_environment GODOT_HOST_ARCH require_environment GODOT_DOTNET require_environment GODOT_PROJECT_PATH require_environment GODOT_EXPORT_PRESET require_environment GODOT_EXPORT_MODE require_environment GODOT_EXPORT_FILE_NAME require_environment GODOT_ACTION_ROOT require_environment GITHUB_WORKSPACE require_environment GITHUB_OUTPUT require_command curl require_command unzip require_command sha512sum validate_common_inputs [[ "$(uname -s)" == "Linux" ]] || fail "The Linux script was selected on a non-Linux runner." [[ "$GODOT_HOST_ARCH" == "x86_32" || "$GODOT_HOST_ARCH" == "x86_64" || "$GODOT_HOST_ARCH" == "arm32" || "$GODOT_HOST_ARCH" == "arm64" ]] || fail "Unsupported Linux host-arch '$GODOT_HOST_ARCH'." [[ "$GODOT_HOST_ARCH" == "$(actual_architecture)" ]] || fail "host-arch '$GODOT_HOST_ARCH' does not match this Linux runner ($(actual_architecture))." [[ "$GODOT_PROJECT_PATH" != /* && "$GODOT_PROJECT_PATH" != \\* ]] || fail "project-path must be relative to the checked-out workspace." WORKSPACE_DIR="$(cd "$GITHUB_WORKSPACE" && pwd -P)" PROJECT_DIR="$(cd "$WORKSPACE_DIR/$GODOT_PROJECT_PATH" 2>/dev/null && pwd -P)" || fail "project-path '$GODOT_PROJECT_PATH' does not exist." case "$PROJECT_DIR/" in "$WORKSPACE_DIR/"*) ;; *) fail "project-path must remain inside the checked-out workspace." ;; esac [[ -f "$PROJECT_DIR/project.godot" ]] || fail "No project.godot was found in project-path '$GODOT_PROJECT_PATH'." [[ -f "$PROJECT_DIR/export_presets.cfg" ]] || fail "No export_presets.cfg was found in project-path '$GODOT_PROJECT_PATH'." grep -Fq "name=\"$GODOT_EXPORT_PRESET\"" "$PROJECT_DIR/export_presets.cfg" || fail "Export preset '$GODOT_EXPORT_PRESET' was not found in export_presets.cfg." if [[ "$GODOT_DOTNET" == "true" ]]; then EDITION=dotnet EDITOR_ASSET="Godot_v${GODOT_VERSION}_mono_linux_${GODOT_HOST_ARCH}.zip" TEMPLATES_ASSET="Godot_v${GODOT_VERSION}_mono_export_templates.tpz" else EDITION=standard EDITOR_ASSET="Godot_v${GODOT_VERSION}_linux.${GODOT_HOST_ARCH}.zip" TEMPLATES_ASSET="Godot_v${GODOT_VERSION}_export_templates.tpz" fi BASE_URL="https://github.com/godotengine/godot-builds/releases/download/$GODOT_VERSION" TEMPLATE_VERSION="${GODOT_VERSION//-/.}" CACHE_DIR="$GODOT_ACTION_ROOT/cache/$GODOT_VERSION/$EDITION/linux-$GODOT_HOST_ARCH" EDITOR_DIR="$CACHE_DIR/editor" USER_DATA_DIR="$CACHE_DIR/user-data" TEMPLATE_DIR="$USER_DATA_DIR/godot/export_templates/$TEMPLATE_VERSION" if [[ -f "$CACHE_DIR/.complete" && -f "$TEMPLATE_DIR/version.txt" ]]; then EDITOR_EXECUTABLE="$(find_editor || true)" else EDITOR_EXECUTABLE="" fi if [[ -z "$EDITOR_EXECUTABLE" ]]; then install_godot fi chmod +x "$EDITOR_EXECUTABLE" SAFE_PRESET="$(printf '%s' "$GODOT_EXPORT_PRESET" | tr -cs '[:alnum:]_.-' '_')" BUILD_DIR="$GODOT_ACTION_ROOT/builds/${SAFE_PRESET:-export}-$GODOT_EXPORT_MODE-$$" OUTPUT_FILE="$BUILD_DIR/$GODOT_EXPORT_FILE_NAME" mkdir -p "$BUILD_DIR" export XDG_DATA_HOME="$USER_DATA_DIR" export XDG_CONFIG_HOME="$CACHE_DIR/config" "$EDITOR_EXECUTABLE" --headless --path "$PROJECT_DIR" "--export-$GODOT_EXPORT_MODE" "$GODOT_EXPORT_PRESET" "$OUTPUT_FILE" [[ -e "$OUTPUT_FILE" ]] || fail "Godot completed without creating the expected output '$GODOT_EXPORT_FILE_NAME'." printf 'build-directory=%s\n' "$BUILD_DIR" >> "$GITHUB_OUTPUT"