Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
198 changes: 156 additions & 42 deletions .github/workflows/create-release.yml
Original file line number Diff line number Diff line change
@@ -1,66 +1,180 @@
# Whenever a tag push matching pattern "v*" then run the job
name: Create Release

# Runs on a "v*" tag push (builds + publishes a draft release), or manually
# (builds only, so the pipeline can be tested without cutting a tag).
on:
push:
tags:
- "v*"
workflow_dispatch:

env:
GODOT_VERSION: 4.5
GODOT_VERSION: "4.5"
GODOT_RELEASE_TYPE: stable
RCEDIT_VERSION: v2.0.0

permissions:
contents: write

jobs:
# job id, can be anything
export_game:
# Always use ubuntu-latest for this action
runs-on: ubuntu-latest
# Add permission for release creation. Can be made narrower according to your needs
permissions: write-all
# Job name, can be anything
name: Export Game
# Pinned rather than ubuntu-latest: an image roll silently changed the
# available wine packages once already.
runs-on: ubuntu-24.04
steps:
# Always include the checkout step so that
# your project is available for Godot to export
- name: checkout
uses: actions/checkout@v3.3.0
- name: Checkout
uses: actions/checkout@v4
with:
submodules: "recursive"
submodules: recursive

# Godot shells out to rcedit (through wine) to stamp the icon and version
# metadata into the Windows .exe, because the "Windows Desktop" preset sets
# application/modify_resources=true. Godot only logs an *info* message when
# rcedit is missing, so a broken wine setup ships an iconless .exe on a green
# build -- hence the hard failure below.
- name: Install Wine
id: wine_install
id: wine
run: |
set -euo pipefail
sudo apt-get update
sudo apt-get install -y --no-install-recommends wine64
echo "WINE_PATH=$(which wine64)" >> $GITHUB_OUTPUT
sudo apt-get install -y --no-install-recommends wine

- name: export game
id: export
# Use latest version (see releases for all versions)
uses: firebelley/godot-export@v5.2.1
with:
# Defining all the required inputs
godot_executable_download_url: https://github.com/godotengine/godot-builds/releases/download/${{ env.GODOT_VERSION }}-${{ env.GODOT_RELEASE_TYPE }}/Godot_v${{ env.GODOT_VERSION }}-${{ env.GODOT_RELEASE_TYPE }}_linux.x86_64.zip
godot_export_templates_download_url: https://github.com/godotengine/godot-builds/releases/download/${{ env.GODOT_VERSION }}-${{ env.GODOT_RELEASE_TYPE }}/Godot_v${{ env.GODOT_VERSION }}-${{ env.GODOT_RELEASE_TYPE }}_export_templates.tpz
relative_project_path: .
archive_output: true
wine_path: ${{ steps.wine_install.outputs.WINE_PATH }}

env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Ubuntu 24.04 dropped the "wine64" binary name in favour of "wine".
WINE_PATH="$(command -v wine64 || command -v wine || true)"
if [ -z "$WINE_PATH" ]; then
echo "::error::Neither 'wine64' nor 'wine' is on PATH after install; rcedit cannot run."
exit 1
fi
echo "Using wine at: $WINE_PATH"
echo "wine_path=$WINE_PATH" >> "$GITHUB_OUTPUT"

# Create the wine prefix up front so the first rcedit call doesn't race it.
# This is an optimisation, not a requirement -- wine builds the prefix on
# first use anyway, so don't fail the build over it.
if command -v wineboot >/dev/null 2>&1; then
WINEDEBUG=-all wineboot --init \
|| echo "::warning::wineboot --init failed; wine will create its prefix on first use."
fi

- name: Download rcedit
run: |
set -euo pipefail
curl -fsSL -o "$HOME/rcedit-x64.exe" \
"https://github.com/electron/rcedit/releases/download/${RCEDIT_VERSION}/rcedit-x64.exe"

- name: generate SHA512-SUMS.txt
# Unpacked outside the workspace: stray files inside the project directory
# would be picked up by Godot's filesystem scan.
- name: Install Godot and export templates
run: |
cd "${{ steps.export.outputs.archive_directory }}"
sha512sum * > SHA512-SUMS.txt
set -euo pipefail
BASE="https://github.com/godotengine/godot-builds/releases/download/${GODOT_VERSION}-${GODOT_RELEASE_TYPE}"
NAME="Godot_v${GODOT_VERSION}-${GODOT_RELEASE_TYPE}"

WORK="$RUNNER_TEMP/godot-setup"
mkdir -p "$WORK" "$HOME/bin"
cd "$WORK"

curl -fsSL -o godot.zip "${BASE}/${NAME}_linux.x86_64.zip"
curl -fsSL -o templates.tpz "${BASE}/${NAME}_export_templates.tpz"

unzip -q godot.zip
mv "${NAME}_linux.x86_64" "$HOME/bin/godot"
chmod +x "$HOME/bin/godot"
echo "$HOME/bin" >> "$GITHUB_PATH"
"$HOME/bin/godot" --version

# The .tpz expands to a top-level "templates/" dir; version.txt inside it
# holds the exact directory name Godot looks for (e.g. "4.5.stable").
unzip -q templates.tpz
TEMPLATE_VERSION="$(cat templates/version.txt)"
echo "Export templates version: ${TEMPLATE_VERSION}"
mkdir -p "$HOME/.local/share/godot/export_templates"
rm -rf "$HOME/.local/share/godot/export_templates/${TEMPLATE_VERSION}"
mv templates "$HOME/.local/share/godot/export_templates/${TEMPLATE_VERSION}"

# rcedit/wine paths live in the editor settings, not in export_presets.cfg.
# Godot 4.5 reads editor_settings-4.5.tres (major.minor) -- the widely copied
# editor_settings-4.tres is silently ignored.
- name: Configure Godot editor settings
run: |
set -euo pipefail
mkdir -p "$HOME/.config/godot"
SETTINGS="$HOME/.config/godot/editor_settings-${GODOT_VERSION}.tres"
cat > "$SETTINGS" <<EOF
[gd_resource type="EditorSettings" format=3]

[resource]
export/windows/rcedit = "$HOME/rcedit-x64.exe"
export/windows/wine = "${{ steps.wine.outputs.wine_path }}"
EOF
echo "Wrote ${SETTINGS}:"
cat "$SETTINGS"

- name: Import project
run: |
set -euo pipefail
# A cold .godot/ can make the first import exit non-zero; retry once.
godot --headless --import || godot --headless --import

- name: Export Linux and Windows builds
run: |
set -euo pipefail
BUILD="$RUNNER_TEMP/build"
mkdir -p "$BUILD/linux" "$BUILD/windows"

# Explicit output paths override the presets' export_path, which points
# outside the repo (../builds/godots/...).
godot --headless --export-release "Linux/X11" "$BUILD/linux/Godots.x86_64"
godot --headless --export-release "Windows Desktop" "$BUILD/windows/Godots.exe"

# No Godots.console.exe here: the preset sets debug/export_console_wrapper=1
# ("Debug Only"), so a release export deliberately produces the GUI exe alone.
#
# Godot can exit 0 having produced nothing; never publish an empty release.
for f in \
"$BUILD/linux/Godots.x86_64" \
"$BUILD/windows/Godots.exe"; do
if [ ! -s "$f" ]; then
echo "::error::Expected export output '$f' is missing or empty."
exit 1
fi
echo "OK: $f ($(stat -c%s "$f") bytes)"
done

- name: Package artifacts
run: |
set -euo pipefail
BUILD="$RUNNER_TEMP/build"
mkdir -p dist

chmod +x "$BUILD/linux/Godots.x86_64"
(cd "$BUILD/linux" && zip -q -X "$GITHUB_WORKSPACE/dist/Godots-linux-x86_64.zip" Godots.x86_64)
(cd "$BUILD/windows" && zip -q -X "$GITHUB_WORKSPACE/dist/Godots-windows-x86_64.zip" Godots.exe)

cd dist
sha512sum *.zip > SHA512-SUMS.txt
echo "Generated SHA512-SUMS.txt:"
cat SHA512-SUMS.txt

# This release action has worked well for me. However, you can most likely use any release action of your choosing.
# https://github.com/ncipollo/release-action
- name: create release
uses: ncipollo/release-action@v1.12.0
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
draft: true
tag: ${{ github.ref_name }}
artifacts: |
${{ steps.export.outputs.archive_directory }}/*
${{ steps.export.outputs.archive_directory }}/SHA512-SUMS.txt
name: godots-${{ github.sha }}
path: dist/
if-no-files-found: error

- name: Create or update draft release
if: startsWith(github.ref, 'refs/tags/')
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ github.ref_name }}
run: |
set -euo pipefail
if gh release view "$TAG" >/dev/null 2>&1; then
echo "Release $TAG already exists; replacing its assets."
gh release upload "$TAG" dist/* --clobber
else
echo "Creating draft release $TAG."
gh release create "$TAG" dist/* --draft --title "$TAG" --generate-notes
fi
25 changes: 16 additions & 9 deletions src/components/editors/local/editor_item/editor_item.gd
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ class_name EditorListItemControl
extends HBoxListItem

signal edited
signal removed(remove_dir: bool)
signal removed(remove_dir: bool, remove_binary: bool)
signal manage_tags_requested
signal tag_clicked(tag: String)

Expand Down Expand Up @@ -299,23 +299,30 @@ func _on_remove(item: LocalEditors.Item) -> void:
warning.self_modulate = get_theme_color("warning_color", "Editor")
warning.hide()

var checkbox := CheckBox.new()
checkbox.text = tr("remove also from the file system")
checkbox.toggled.connect(func(toggled: bool) -> void:
var remove_binary_checkbox := CheckBox.new()
remove_binary_checkbox.text = tr("delete binary from filesystem")

var remove_dir_checkbox := CheckBox.new()
remove_dir_checkbox.text = tr("delete parent folder in filesystem")
remove_dir_checkbox.toggled.connect(func(toggled: bool) -> void:
warning.visible = toggled
if toggled:
remove_binary_checkbox.button_pressed = false
remove_binary_checkbox.disabled = toggled
)

var vb := VBoxContainer.new()
vb.add_child(label)
vb.add_child(checkbox)
vb.add_child(remove_binary_checkbox)
vb.add_child(remove_dir_checkbox)
vb.add_child(warning)
vb.add_spacer(false)

confirmation_dialog.add_child(vb)

confirmation_dialog.confirmed.connect(func() -> void:
queue_free()
removed.emit(checkbox.button_pressed)
removed.emit(remove_dir_checkbox.button_pressed, remove_binary_checkbox.button_pressed)
)
add_child(confirmation_dialog)
confirmation_dialog.popup_centered()
Expand Down
4 changes: 2 additions & 2 deletions src/components/editors/local/editors_list/editors_list.gd
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
class_name EditorsVBoxList
extends VBoxList

signal item_removed(item_data: LocalEditors.Item, remove_dir: bool)
signal item_removed(item_data: LocalEditors.Item, remove_dir: bool, remove_binary: bool)
signal item_edited(item_data: LocalEditors.Item)
signal item_manage_tags_requested(item_data: LocalEditors.Item)

Expand All @@ -10,7 +10,7 @@ func _post_add(raw_item_data: Object, raw_item_control: Control) -> void:
var item_data := raw_item_data as LocalEditors.Item
var item_control := raw_item_control as EditorListItemControl
item_control.removed.connect(
func(remove_dir: bool) -> void: item_removed.emit(item_data, remove_dir)
func(remove_dir: bool, remove_binary: bool) -> void: item_removed.emit(item_data, remove_dir, remove_binary)
)
item_control.edited.connect(
func() -> void: item_edited.emit(item_data)
Expand Down
36 changes: 27 additions & 9 deletions src/components/editors/local/local_editors.gd
Original file line number Diff line number Diff line change
Expand Up @@ -197,24 +197,42 @@ func _on_editors_list_item_selected(item: EditorListItemControl) -> void:
_sidebar.refresh_actions(item.get_actions())


func _on_editors_list_item_removed(item_data: LocalEditors.Item, remove_dir: bool) -> void:
func _on_editors_list_item_removed(item_data: LocalEditors.Item, remove_dir: bool, remove_binary: bool) -> void:
var target := _resolve_removal_target(
item_data.path, Config.VERSIONS_PATH.ret() as String, remove_dir, remove_binary
)
if remove_dir:
var base_dir := ProjectSettings.globalize_path(item_data.path.get_base_dir())
var versions_dir := ProjectSettings.globalize_path(Config.VERSIONS_PATH.ret() as String)
if not OS.has_feature("linux"):
base_dir = base_dir.to_lower()
versions_dir = versions_dir.to_lower()
if base_dir != versions_dir and base_dir.begins_with(versions_dir):
edir.remove_recursive(base_dir)
if target != "":
edir.remove_recursive(target)
else:
Output.push("skipping removing path {%s}" % base_dir)
Output.push("skipping removing path {%s}" % ProjectSettings.globalize_path(item_data.path.get_base_dir()))
elif remove_binary:
DirAccess.remove_absolute(target)
if _local_editors.has(item_data.path):
_local_editors.erase(item_data.path)
_local_editors.save()
_sidebar.refresh_actions([])
_update_remove_missing_disabled()


# Returns the absolute path to delete from the filesystem, or "" if nothing should be
# removed. The parent folder is only removable when it is a sub-folder of the versions
# dir (never the versions dir itself, e.g. for "extract to same folder" installs).
static func _resolve_removal_target(editor_path: String, versions_path: String, remove_dir: bool, remove_binary: bool) -> String:
if remove_dir:
var base_dir := ProjectSettings.globalize_path(editor_path.get_base_dir())
var versions_dir := ProjectSettings.globalize_path(versions_path)
if not OS.has_feature("linux"):
base_dir = base_dir.to_lower()
versions_dir = versions_dir.to_lower()
if base_dir != versions_dir and base_dir.begins_with(versions_dir):
return base_dir
return ""
if remove_binary:
return ProjectSettings.globalize_path(editor_path)
return ""


func _on_editors_list_item_edited(item_data: Variant) -> void:
_local_editors.save()
_editors_list.sort_items()
Expand Down
18 changes: 14 additions & 4 deletions src/components/editors/remote/remote_editors.gd
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,19 @@ func install_zip(zip_abs_path: String, root_unzip_folder_name: String, possible_


func _unzip_downloaded(downloaded_abs_path: String, root_unzip_folder_name: String) -> String:
var zip_content_dir := "%s/%s" % [Config.VERSIONS_PATH.ret(), root_unzip_folder_name]
if DirAccess.dir_exists_absolute(ProjectSettings.globalize_path(zip_content_dir)):
zip_content_dir += "-%s" % uuid.v4().substr(0, 8)
zip_content_dir += "/"
var zip_content_dir := _resolve_unzip_dir(
Config.VERSIONS_PATH.ret() as String,
root_unzip_folder_name,
Config.EXTRACT_TO_SAME_FOLDER.ret() as bool
)
zip.unzip(downloaded_abs_path, zip_content_dir)
return zip_content_dir


static func _resolve_unzip_dir(versions_path: String, root_unzip_folder_name: String, extract_to_same_folder: bool) -> String:
if extract_to_same_folder:
return versions_path + "/"
var zip_content_dir := "%s/%s" % [versions_path, root_unzip_folder_name]
if DirAccess.dir_exists_absolute(ProjectSettings.globalize_path(zip_content_dir)):
zip_content_dir += "-%s" % uuid.v4().substr(0, 8)
return zip_content_dir + "/"
6 changes: 6 additions & 0 deletions src/components/settings/settings_window.gd
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,12 @@ func _prepare_settings() -> Array:
SettingCheckbox,
tr("Will check only stable Godots releases.")
)),
SettingChangeObserved(SettingCfg(
"application/advanced/extract_to_same_folder",
Config.EXTRACT_TO_SAME_FOLDER,
SettingCheckbox,
tr("Extract downloaded editors directly into the versions dir instead of creating a subfolder for each one.")
)),
SettingChangeObserved(SettingCfg(
"network/http_proxy/host",
Config.HTTP_PROXY_HOST,
Expand Down
Loading