Fix NSIS installer build: absolute paths via defines, space-free installed exe, BMP ICO

- makensis resolves relative -DICON/-DARTIFACT against the script directory,
  so CI now passes $PWD-based absolute paths (reproduced locally in the same
  ubuntu:24.04 container; installer builds, 7z shows step-line.exe +
  uninstall.exe)
- Install as step-line.exe (no spaces: NSIS /oname splits on whitespace)
- OutFile overridable via -DOUT
- tools/generate_textures.py icon.ico now produced via ImageMagick with
  classic BMP entries (NSIS-compatible); PIL PNG-compressed ICOs rejected by
  NSIS
This commit is contained in:
zwitschi committed 2026-10-31 14:22:55 +01:00
1 parent 8bc84c1f36
commit 7751258184
12 files changed
+41 -15

No files matched your search

+6 -4
View File
@@ -82,19 +82,21 @@ jobs:
sudo apt-get update && sudo apt-get install -y nsis p7zip-full sudo apt-get update && sudo apt-get install -y nsis p7zip-full
VERSION="$(sed -n 's/.*config\/version="\([^"]*\)".*/\1/p' project.godot)" VERSION="$(sed -n 's/.*config\/version="\([^"]*\)".*/\1/p' project.godot)"
# NOTE: on Linux, makensis uses -D defines (the /D form is Windows-only # NOTE: on Linux, makensis uses -D defines (the /D form is Windows-only
# and is rejected as a script path). # and is rejected as a script path), and it resolves relative paths
# against the script directory — pass absolute paths from the workspace.
cp assets/textures/icon.ico packaging/windows/step-line.ico cp assets/textures/icon.ico packaging/windows/step-line.ico
makensis \ makensis \
-DVERSION="$VERSION" \ -DVERSION="$VERSION" \
-DARTIFACT="build/step-line-windows-$VERSION.exe" \ -DARTIFACT="$PWD/build/step-line-windows-$VERSION.exe" \
-DICON=packaging/windows/step-line.ico \ -DICON="$PWD/packaging/windows/step-line.ico" \
-DOUT="$PWD/build/step-line-setup-$VERSION.exe" \
packaging/windows/step-line.nsi packaging/windows/step-line.nsi
- name: Verify Windows installer contents - name: Verify Windows installer contents
run: | run: |
VERSION="$(sed -n 's/.*config\/version="\([^"]*\)".*/\1/p' project.godot)" VERSION="$(sed -n 's/.*config\/version="\([^"]*\)".*/\1/p' project.godot)"
ls -la build/step-line-setup-$VERSION.exe ls -la build/step-line-setup-$VERSION.exe
7z l build/step-line-setup-$VERSION.exe | grep -E "Step Line.exe|uninstall.exe" 7z l build/step-line-setup-$VERSION.exe | grep -E "step-line.exe|uninstall.exe"
- name: Package Linux AppImage - name: Package Linux AppImage
run: | run: |
Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 225 KiB

After

Width:  |  Height:  |  Size: 191 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 233 KiB

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 247 KiB

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 220 KiB

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 233 KiB

After

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 266 KiB

After

Width:  |  Height:  |  Size: 237 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 15 KiB

+7 -3
View File
@@ -14,8 +14,12 @@
!define ICON "packaging\windows\step-line.ico" !define ICON "packaging\windows\step-line.ico"
!endif !endif
; The installed executable always uses this clean name. ; The installed executable uses this clean name (no spaces — NSIS /oname tokens
!define INSTALLED_EXE "Step Line.exe" ; on whitespace, so a spaced name breaks the File directive).
!define INSTALLED_EXE "step-line.exe"
!ifndef OUT
!define OUT "build\step-line-setup-${VERSION}.exe"
!endif
!define COMPANY "Step Line Project" !define COMPANY "Step Line Project"
!define UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\Step Line" !define UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\Step Line"
@@ -25,7 +29,7 @@ Icon "${ICON}"
UninstallIcon "${ICON}" UninstallIcon "${ICON}"
Name "Step Line ${VERSION}" Name "Step Line ${VERSION}"
OutFile "build\step-line-setup-${VERSION}.exe" OutFile "${OUT}"
InstallDir "$PROGRAMFILES64\Step Line" InstallDir "$PROGRAMFILES64\Step Line"
RequestExecutionLevel admin RequestExecutionLevel admin
+5 -3
View File
@@ -140,7 +140,7 @@ func _init() -> void:
# --- body-camera overlay (REC + shift clock + corner brackets, top right) --- # --- body-camera overlay (REC + shift clock + corner brackets, top right) ---
var cam_box := VBoxContainer.new() var cam_box := VBoxContainer.new()
cam_box.set_anchors_preset(Control.PRESET_TOP_RIGHT) cam_box.set_anchors_preset(Control.PRESET_TOP_RIGHT)
cam_box.position = Vector2(-180, 12) cam_box.position = Vector2(-180, 50)
cam_box.alignment = BoxContainer.ALIGNMENT_END cam_box.alignment = BoxContainer.ALIGNMENT_END
add_child(cam_box) add_child(cam_box)
var rec_row := HBoxContainer.new() var rec_row := HBoxContainer.new()
@@ -380,6 +380,7 @@ func show_inspection(controller: Object, artifacts: Array, result: Dictionary) -
continue continue
var card := TextureRect.new() var card := TextureRect.new()
card.texture = tex card.texture = tex
card.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
card.custom_minimum_size = Vector2(92, 58) card.custom_minimum_size = Vector2(92, 58)
card.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED card.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
card.tooltip_text = artifact.summary() card.tooltip_text = artifact.summary()
@@ -388,7 +389,8 @@ func show_inspection(controller: Object, artifacts: Array, result: Dictionary) -
card_row.add_child(card) card_row.add_child(card)
added_card = true added_card = true
if added_card: if added_card:
inspect_panel.add_child(card_row) var content := inspect_panel.get_child(0) as VBoxContainer
content.add_child(card_row)
var lines: Array[String] = [] var lines: Array[String] = []
for artifact in artifacts: for artifact in artifacts:
lines.append(" • " + artifact.summary()) lines.append(" • " + artifact.summary())
@@ -409,7 +411,7 @@ func _clear_card_row() -> void:
return return
var row := inspect_panel.find_child("INSPECT_CARD_ROW", true, false) as Control var row := inspect_panel.find_child("INSPECT_CARD_ROW", true, false) as Control
if row: if row:
inspect_panel.remove_child(row) row.get_parent().remove_child(row)
row.queue_free() row.queue_free()
func show_wand(controller: Object, result: Dictionary) -> void: func show_wand(controller: Object, result: Dictionary) -> void:
+23 -5
View File
@@ -306,12 +306,30 @@ def app_icon():
icon("icon.png", badge, size=1024) icon("icon.png", badge, size=1024)
icon("icon.ico", badge, size=256) icon("icon.ico", badge, size=256)
# ICO for Windows. Sizes stay at 16/32/48 so every entry uses classic BMP # ICO for Windows. Must be BMP-entry based: PIL's ICO serializer emits
# (PNG-compressed entries, used by PIL at >=256px, break some NSIS builds). # PNG-compressed entries which NSIS' icon loader rejects. ImageMagick
src = Image.open(os.path.join(ASSETS, "icon.png")).convert("RGBA") # writes classic BMP entries; fall back to PIL only if ImageMagick is gone
# (flag it so a human notices).
import subprocess
import sys
ico_path = os.path.join(ASSETS, "icon.ico") ico_path = os.path.join(ASSETS, "icon.ico")
src.save(ico_path, format="ICO", sizes=[(48, 48), (32, 32), (16, 16)]) src_png = os.path.join(ASSETS, "icon.png")
print("wrote assets/textures/icon.ico (BMP 16/32/48)") ran = False
for cmd in (
["magick", src_png, "-background", "none", "-define", "icon:auto-resize=48,32,16", ico_path],
["convert", src_png, "-background", "none", "-define", "icon:auto-resize=48,32,16", ico_path],
):
try:
subprocess.run(cmd, check=True, capture_output=True)
ran = True
break
except (FileNotFoundError, subprocess.CalledProcessError):
continue
if ran:
print("wrote assets/textures/icon.ico (BMP 16/32/48 via ImageMagick)")
else:
print("WARNING: ImageMagick not found; writing PIL ICO (may be PNG-compressed)")
Image.open(src_png).convert("RGBA").save(ico_path, format="ICO", sizes=[(48, 48), (32, 32), (16, 16)])
def cards(): def cards():