diff --git a/build-release.sh b/build-release.sh index e9af147..946571a 100755 --- a/build-release.sh +++ b/build-release.sh @@ -3,6 +3,9 @@ set -e export basedir=$(pwd) +# Log output to a file automatically. +exec > >(tee -a "out/logs/build-release") 2>&1 + # Config # For signing keystore and password. @@ -266,13 +269,6 @@ if [ "${build_classical}" == "1" ]; then zip -q -9 "${reldir}/${binname}.zip" ${binname} ${wrpname} rm ${binname} ${wrpname} - if [ -d out/windows/steam ]; then - cp out/windows/steam/godot.windows.editor.x86_64.exe ${steamdir}/godot.windows.opt.tools.64.exe - cp out/windows/steam/godot.windows.editor.x86_32.exe ${steamdir}/godot.windows.opt.tools.32.exe - sign_windows ${steamdir}/godot.windows.opt.tools.64.exe - sign_windows ${steamdir}/godot.windows.opt.tools.32.exe - fi - # Templates cp out/windows/x86_64/templates/godot.windows.template_release.x86_64.exe ${templatesdir}/windows_release_x86_64.exe cp out/windows/x86_64/templates/godot.windows.template_debug.x86_64.exe ${templatesdir}/windows_debug_x86_64.exe @@ -312,6 +308,20 @@ if [ "${build_classical}" == "1" ]; then rm -rf macos_template.app sign_macos_template ${templatesdir} 0 + ## Steam (Classical) ## + + if [ -d out/windows/steam ]; then + cp out/windows/steam/godot.windows.editor.x86_64.exe ${steamdir}/godot.windows.opt.tools.64.exe + cp out/windows/steam/godot.windows.editor.x86_32.exe ${steamdir}/godot.windows.opt.tools.32.exe + sign_windows ${steamdir}/godot.windows.opt.tools.64.exe + sign_windows ${steamdir}/godot.windows.opt.tools.32.exe + unzip ${reldir}/${godot_basename}_linux.x86_64.zip -d ${steamdir}/ + unzip ${reldir}/${godot_basename}_linux.x86_32.zip -d ${steamdir}/ + mv ${steamdir}/{${godot_basename}_linux.x86_64,godot.x11.opt.tools.64} + mv ${steamdir}/{${godot_basename}_linux.x86_32,godot.x11.opt.tools.32} + unzip ${reldir}/${godot_basename}_macos.universal -d ${steamdir}/ + fi + ## Web (Classical) ## # Editor diff --git a/build.sh b/build.sh index 97137e9..aefb41b 100755 --- a/build.sh +++ b/build.sh @@ -4,6 +4,14 @@ set -e OPTIND=1 +export basedir="$(pwd)" +mkdir -p ${basedir}/out +mkdir -p ${basedir}/out/logs +mkdir -p ${basedir}/mono-glue + +# Log output to a file automatically. +exec > >(tee -a "out/logs/build") 2>&1 + # Config # For default registry and number of cores. @@ -239,11 +247,6 @@ EOF popd fi -export basedir="$(pwd)" -mkdir -p ${basedir}/out -mkdir -p ${basedir}/out/logs -mkdir -p ${basedir}/mono-glue - export podman_run="${podman} run -it --rm --env BUILD_NAME=${BUILD_NAME} --env GODOT_VERSION_STATUS=${GODOT_VERSION_STATUS} --env NUM_CORES=${NUM_CORES} --env CLASSICAL=${build_classical} --env MONO=${build_mono} -v ${basedir}/godot-${godot_version}.tar.gz:/root/godot.tar.gz -v ${basedir}/mono-glue:/root/mono-glue -w /root/" export img_version=$IMAGE_VERSION diff --git a/config.sh.in b/config.sh.in index 392bee1..8ba7187 100644 --- a/config.sh.in +++ b/config.sh.in @@ -12,10 +12,25 @@ # relevant tool in your PATH or an absolute path to run it from. export PODMAN='podman' +# GitHub token for @GodotBuilder for releases on godotengine/godot-builds, +# can use a long expiry date. +# For stable releases to godotengine/godot, generate a personal token with +# write access to godotengine/godot for use in publish-release.sh. +export GH_TOKEN='' + # Path to a Git clone of https://github.com/godotengine/godot-builds. # Only used for uploading official releases. export GODOT_BUILDS_PATH='' +# Path to the directory with the Steam upload setup. +export UPLOAD_STEAM_PATH='' + +# Path to the directory with the EGS upload setup. +export UPLOAD_EGS_PATH='' + +# Path to the 'butler' binary for upload of stable releases to itch.io. +export UPLOAD_ITCH_BUTLER='' + # SSH hostname to upload Web editor builds to. # Only used for uploading official releases. export WEB_EDITOR_HOSTNAME='' diff --git a/publish-release.sh b/publish-release.sh index 00e9ea5..22a8e5e 100755 --- a/publish-release.sh +++ b/publish-release.sh @@ -2,21 +2,26 @@ set -e +# Log output to a file automatically. +exec > >(tee -a "out/logs/publish-release") 2>&1 + # Config -# For signing keys, and path to godot-builds repo. +# For upload tools and signing/release keys. source ./config.sh godot_version="" -web_editor_latest=0 +latest_stable=0 +skip_stable=0 -while getopts "h?v:l" opt; do +while getopts "h?v:ls" opt; do case "$opt" in h|\?) echo "Usage: $0 [OPTIONS...]" echo echo " -v godot version (e.g: 3.2-stable) [mandatory]" - echo " -l mark web editor as latest" + echo " -l latest stable release (web editor, itch.io, EGS)" + echo " -s don't run stable specific steps" echo exit 1 ;; @@ -24,7 +29,10 @@ while getopts "h?v:l" opt; do godot_version=$OPTARG ;; l) - web_editor_latest=1 + latest_stable=1 + ;; + s) + skip_stable=1 ;; esac done @@ -34,6 +42,9 @@ if [ -z "${godot_version}" ]; then exit 1 fi +basedir=$(pwd) +reldir=${basedir}/releases/${godot_version} + # Confirm IFS=- read version status <<< "${godot_version}" @@ -46,8 +57,44 @@ case "$choice" in esac template_version=${version}.${status} +# Config checks for stable releases. + +if [ "${status}" == "stable" -a "${skip_stable}" == "0" ]; then + echo "Publishing a stable release. Checking that configuration is valid to perform stable release specific steps." + + read -p "Enter personal access token (GH_TOKEN) for godotengine/godot: " personal_gh_token + if [[ "${personal_gh_token}" != "github_pat_"* ]]; then + echo "Provided personal access token should start with 'github_pat', aborting." + exit 1 + fi + + if ! gh api repos/godotengine/godot/git/refs/tags | grep -q ${godot_version}; then + echo "The tag '${godot_version}' does not exist in godotengine/godot, aborting." + echo "Push commits and create it manually before running this script." + exit 1 + fi + + if [ ! -d "${UPLOAD_STEAM_PATH}" ]; then + echo "Invalid config.sh: UPLOAD_STEAM_PATH is not a directory, aborting." + exit 1 + fi + + if [ "${latest_stable}" == "1" ]; then + if [ ! -d "${UPLOAD_EGS_PATH}" ]; then + echo "Invalid config.sh: UPLOAD_EGS_PATH is not a directory, aborting." + exit 1 + fi + if [ ! -x "${UPLOAD_ITCH_BUTLER}" ]; then + echo "Invalid config.sh: UPLOAD_ITCH_BUTLER does not point to an executable, aborting." + exit 1 + fi + fi +fi + # Upload to GitHub godot-builds +echo "Uploading release to to godotengine/godot-builds repository." + if [ -z "${GODOT_BUILDS_PATH}" ]; then echo "Missing path to godotengine/godot-builds clone in config.sh, necessary to upload releases. Aborting." exit 1 @@ -55,17 +102,64 @@ fi ${GODOT_BUILDS_PATH}/tools/upload-github.sh -v ${version} -f ${status} -# Web editor +# Stable release only -scp -P 22 -r web/${template_version} ${WEB_EDITOR_HOSTNAME}:/home/akien/web_editor/ -sleep 2 -command="sudo mv /home/akien/web_editor/${template_version} /var/www/editor.godotengine.org/public/releases/" -command="${command}; cd /var/www/editor.godotengine.org; sudo chown -R www-data:www-data public/releases/${template_version}" -command="${command}; sudo ./create-symlinks.sh -v ${template_version}" -if [ $web_editor_latest == 1 ]; then - command="${command} -l" +if [ "${status}" == "stable" -a "${skip_stable}" == "0" ]; then + namever=Godot_v${godot_version} + + echo "Uploading stable release to main GitHub repository." + + export GH_TOKEN=${personal_gh_token} + pushd git + # Get release details from existing godot-builds release. + release_info=$(gh release view ${godot_version} --repo godotengine/godot-builds --json name,body) + release_title=$(echo "$release_info" | jq -r '.name') + release_desc=$(echo "$release_info" | jq -r '.body') + + gh release create ${godot_version} --repo godotengine/godot --title "$release_title" --notes "$release_desc" + gh release upload ${godot_version} ${reldir}/[Gg]* ${reldir}/mono/[Gg]* + # Concatenate SHA sums. + cp ${reldir}/SHA512-SUMS.txt . + cat ${reldir}/mono/SHA512-SUMS.txt >> SHA512-SUMS.txt + gh release upload ${godot_version} SHA512-SUMS.txt + rm SHA512-SUMS.txt + popd + + echo "Uploading stable release to Steam." + + pushd ${UPLOAD_STEAM_PATH} + rm -rf content/bin/[Gg]* + rm -rf content/editor_data/export_templates/* + cp -f ${basedir}/git/*.{md,txt,png,svg} content/ + # Steam specific binaries prepared by build-release.sh + cp -r ${basedir}/steam/[Gg]* content/bin/ + unzip ${reldir}/${namever}_export_templates.tpz -d content/editor_data/export_templates/ + mv content/editor_data/export_templates/{templates,${template_version}} + steam_build/build.sh + popd + + if [ "${latest_stable}" == "1" ]; then + echo "Uploading stable release to EGS (latest only)." + + pushd ${UPLOAD_EGS_PATH} + rm -rf buildroot-*/* + unzip ${reldir}/${namever}_win64.exe.zip -d buildroot-win64/ + unzip ${reldir}/${namever}_win32.exe.zip -d buildroot-win32/ + unzip ${reldir}/${namever}_macos.universal.zip -d buildroot-macos/ + ./upload.sh -v ${godot_version} + popd + + echo "Uploading stable release to itch.io (latest only)." + + ${UPLOAD_ITCH_BUTLER} push ${reldir}/${namever}_linux.x86_64.zip godotengine/godot:linux-64-stable --userversion ${godot_version} + ${UPLOAD_ITCH_BUTLER} push ${reldir}/${namever}_linux.x86_32.zip godotengine/godot:linux-32-stable --userversion ${godot_version} + ${UPLOAD_ITCH_BUTLER} push ${reldir}/${namever}_win64.exe.zip godotengine/godot:windows-64-stable --userversion ${godot_version} + ${UPLOAD_ITCH_BUTLER} push ${reldir}/${namever}_win32.exe.zip godotengine/godot:windows-32-stable --userversion ${godot_version} + ${UPLOAD_ITCH_BUTLER} push ${reldir}/${namever}_macos.universal.zip godotengine/godot:osx-64-stable --userversion ${godot_version} + fi + + echo "All stable release upload steps done." fi -ssh -P 22 ${WEB_EDITOR_HOSTNAME} "${command}" # NuGet packages @@ -91,8 +185,40 @@ else echo "Disabling Android library publishing as deps/keystore doesn't exist." fi -# Stable release only +# Web editor -if [ "${status}" == "stable" ]; then - echo "NOTE: This script doesn't handle yet uploading stable releases to the main GitHub repository, Steam, EGS, and itch.io." +echo "Uploading web editor... (with retry logic as it can be flaky)" + +MAX_RETRIES=5 +delay=5 + +retry_command() { + local attempt=1 + local cmd=$1 + while [ ${attempt} -le ${MAX_RETRIES} ]; do + echo "Attempt ${attempt}: Running command..." + eval "${cmd}" && return 0 # Success + + echo "Command failed. Retrying in ${delay} seconds..." + sleep ${delay} + ((attempt++)) + delay=$((delay * 2)) # Exponential backoff + done + + echo "❌ Command failed after ${MAX_RETRIES} attempts." + return 1 +} + +command="sudo mv /home/akien/web_editor/${template_version} /var/www/editor.godotengine.org/public/releases/" +command="${command}; cd /var/www/editor.godotengine.org; sudo chown -R www-data:www-data public/releases/${template_version}" +command="${command}; sudo ./create-symlinks.sh -v ${template_version}" +if [ "${latest_stable}" == "1" ]; then + echo "Marking web editor build as 'latest'." + command="${command} -l" fi + +retry_command "scp -P 22 -r web/${template_version} ${WEB_EDITOR_HOSTNAME}:/home/akien/web_editor/" +sleep 2 +retry_command "ssh -p 22 ${WEB_EDITOR_HOSTNAME} '${command}'" + +echo "All publishing steps done. Check out/logs/publish-release to double check that all steps succeeded."