Skip to content

Commit

Permalink
Multi-platform releases and custom model installation (LagoLunatic#230)
Browse files Browse the repository at this point in the history
* Support other platforms in build.py

* Automated builds via GitHub Actions

* Add Windows and MacOS CI building

* Try 3.9 for MacOS

* Revert to 3.8 for Mac, use my fork of PyFastTextureUtils

* Try to build .app for macos

* Update PyFastTextureUtils commit

* Update PyFastTextureUtils commit to upstream

* Update PyInstaller to 4.2 (fixes building for macOS)

* Use shutil to make zip instead of Zipfile
This properly copies the mac .app bundle as a directory while preserving executable permissions

* Place settings.txt in the correct place when running from a macOS .app

* Bundle as .app for macOS

* Fix typo

* Test uploading artifacts

* Set mac deployment target, glob for artifact

* Use miniconda for macos

* Set build env variable

* Force python3 for macos build

* Put env variable back

* More conda stuff

* Correct name

* This shouldn't be here

* Try conda install

* check something

* try python3

* Please activate conda

* Activate this shell

* Activate all shells

* Upload Linux and Windows artifacts

* Fix typo, remove unneeded lines

* Use appdirs for settings and custom models

* Upload regular unix executable artifact for testing purposes

* Use userdata path for macOS only, remove unneeded code

* Don't upload this artifact anymore

* Custom model zip installation

* Update PyFastTextureUtils

* Verify contents of model zip file before unpacking

* Don't bundle models folder for Mac

* Add pushButton to install a custom model pack

* Hook up model install button

* add certifi to fix issue with ssl when built through github actions, bump PyInstaller version

* Switch to new model after installing it

* Remove unneeded comment and line spacing from workflow

* Update player model after user clicks "OK"

* Support custom model packs containing multiple models

* Create userdata path if it does not exist

* Try zipping in github action

Co-authored-by: EthanArmbrust <EthanArmbrust@github.com>
Co-authored-by: LagoLunatic <LagoLunatic@users.noreply.github.com>
  • Loading branch information
3 people authored Jul 4, 2021
1 parent 7a1d34b commit a0ae163
Show file tree
Hide file tree
Showing 11 changed files with 288 additions and 56 deletions.
93 changes: 93 additions & 0 deletions .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
name: Build

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

jobs:
linux:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Add release key
run: echo ${{ secrets.RELEASE_KEY }} > keys/build_key.txt
- name: Set up Python 3.8
uses: actions/setup-python@v2
with:
python-version: 3.8
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements_full.txt
- name: Build Linux App
run: python -m PyInstaller wwrando.spec
- name: Bundle Linux App
run: python build.py
- uses: actions/upload-artifact@v2
with:
name: wwrando-linux
path: dist/release_archive/*

macos:
runs-on: macos-latest

env:
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
MACOSX_DEPLOYMENT_TARGET: 10.9

steps:
- uses: actions/checkout@v2
- name: Add release key
run: echo ${{ secrets.RELEASE_KEY }} > keys/build_key.txt
- name: Set up Miniconda with Python 3.8
uses: goanpeca/setup-miniconda@v1
with:
architecture: x64
python-version: '3.8'
miniconda-version: latest
- run: |
conda create -qyf -n py38 python=3.8 wheel -c anaconda
conda activate py38
shell: bash -l {0}
- name: Install dependencies
run: |
conda install pip
python3 -m pip install -r requirements_full.txt
shell: bash -l {0}
- name: Build MacOS App
run: python3 -m PyInstaller --onefile --windowed wwrando.spec
shell: bash -l {0}
- name: Bundle MacOS App
run: python3 build.py
shell: bash -l {0}
- uses: actions/upload-artifact@v2
with:
name: wwrando-macos
path: dist/release_archive/*

windows:
runs-on: windows-latest

steps:
- uses: actions/checkout@v2
- name: Add release key
run: echo ${{ secrets.RELEASE_KEY }} > keys/build_key.txt
- name: Set up Python 3.8
uses: actions/setup-python@v2
with:
python-version: 3.8
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements_full.txt
- name: Build Windows App
run: python -m PyInstaller wwrando.spec
- name: Bundle Windows App
run: python build.py
- uses: actions/upload-artifact@v2
with:
name: wwrando-windows
path: dist/release_archive/*
Binary file added assets/icon.icns
Binary file not shown.
35 changes: 27 additions & 8 deletions build.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

from zipfile import ZipFile
import os
import platform
import shutil

from randomizer import VERSION_WITHOUT_COMMIT

Expand All @@ -15,13 +16,31 @@
base_name_with_version += "_32bit"
base_zip_name = base_name_with_version

zip_name = base_zip_name.replace(" ", "_") + ".zip"
exe_ext = ""
if platform.system() == "Windows":
exe_ext = ".exe"
platform_name = "win"
if platform.system() == "Darwin":
exe_ext = ".app"
platform_name = "mac"
if platform.system() == "Linux":
platform_name = "linux"

exe_path = "./dist/%s.exe" % base_name_with_version
if not os.path.isfile(exe_path):
exe_path = "./dist/%s" % base_name_with_version + exe_ext
if not (os.path.isfile(exe_path) or os.path.isdir(exe_path)):
raise Exception("Executable not found: %s" % exe_path)

with ZipFile("./dist/" + zip_name, "w") as zip:
zip.write(exe_path, arcname="%s.exe" % base_name)
zip.write("README.md", arcname="README.txt")
zip.write("./models/About Custom Models.txt", arcname="./models/About Custom Models.txt")
if os.path.exists("./dist/release_archive") and os.path.isdir("./dist/release_archive"):
shutil.rmtree("./dist/release_archive")

os.mkdir("./dist/release_archive")
shutil.copyfile("README.md", "./dist/release_archive/README.txt")

if platform.system() == "Darwin":
shutil.copytree(exe_path, "./dist/release_archive/%s" % base_name + exe_ext)
shutil.copyfile("./models/About Custom Models.txt", "./dist/release_archive/About Custom Models.txt")
else:
os.mkdir("./dist/release_archive/models")
shutil.copyfile(exe_path, "./dist/release_archive/%s" % base_name + exe_ext)
shutil.copyfile("./models/About Custom Models.txt", "./dist/release_archive/models/About Custom Models.txt")

19 changes: 19 additions & 0 deletions keys/generate_keys.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
python generate_key.py > temp_key
key_1= < temp_key
rm temp_key

python generate_key.py > temp_key
key_2= < temp_key
rm temp_key

python generate_key.py > temp_key
key_3= < temp_key
rm temp_key

python generate_key.py > temp_key
key_4= < temp_key
rm temp_key

echo SEED_KEY=str(0X%key_1%-(0X%key_2%+0X%key_3%)/0X%key_4%) > seed_key.py

python generate_key.py > build_key.txt
4 changes: 3 additions & 1 deletion requirements_full.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
appdirs==1.4.4
certifi==2020.12.5
PySide2==5.15.2
PyYAML==5.4
Pillow==8.2.0
PyInstaller==4.1
PyInstaller==4.3
tinyaes==1.0.1
git+git://github.com/LagoLunatic/PyFastBTI.git@da01c14562a4711e031fd0a01c52d2131035e228#egg=PyFastBTI
git+git://github.com/LagoLunatic/PyFastTextureUtils.git@4e6b9db2f7703f016225971f7842f7fd29a1a8a5#egg=PyFastTextureUtils
70 changes: 65 additions & 5 deletions wwr_ui/randomizer_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import base64
import colorsys
import time
import zipfile
import shutil

import yaml
try:
Expand Down Expand Up @@ -48,7 +50,7 @@ def __init__(self, cmd_line_args=OrderedDict()):
self.no_ui_test = ("-noui" in cmd_line_args)
self.profiling = ("-profile" in cmd_line_args)
self.auto_seed = ("-autoseed" in cmd_line_args)

self.custom_color_selector_buttons = OrderedDict()
self.custom_color_selector_hex_inputs = OrderedDict()
self.custom_color_reset_buttons = OrderedDict()
Expand Down Expand Up @@ -84,7 +86,8 @@ def __init__(self, cmd_line_args=OrderedDict()):
self.ui.clean_iso_path_browse_button.clicked.connect(self.browse_for_clean_iso)
self.ui.output_folder_browse_button.clicked.connect(self.browse_for_output_folder)
self.ui.permalink.textEdited.connect(self.permalink_modified)


self.ui.install_custom_model.clicked.connect(self.install_custom_model_zip)
self.ui.custom_player_model.currentIndexChanged.connect(self.custom_model_changed)
self.ui.player_in_casual_clothes.clicked.connect(self.in_casual_clothes_changed)
self.ui.randomize_all_custom_colors_together.clicked.connect(self.randomize_all_custom_colors_together)
Expand Down Expand Up @@ -127,7 +130,7 @@ def __init__(self, cmd_line_args=OrderedDict()):

if self.auto_seed:
self.generate_seed()

if self.no_ui_test:
self.randomize()
return
Expand Down Expand Up @@ -728,7 +731,11 @@ def initialize_custom_player_model_list(self):
self.ui.custom_player_model.addItem("Random (exclude Link)")
else:
self.ui.custom_player_model.setEnabled(False)


def update_custom_player_model_list(self):
self.ui.custom_player_model.clear()
self.initialize_custom_player_model_list()

def initialize_color_presets_list(self):
self.ui.custom_color_preset.addItem("Default")
self.ui.custom_color_preset.addItem("Custom")
Expand Down Expand Up @@ -1310,7 +1317,60 @@ def update_model_preview(self):
qimage = QImage(data, preview_image.width, preview_image.height, QImage.Format_ARGB32)
scaled_pixmap = QPixmap.fromImage(qimage).scaled(225, 350, Qt.KeepAspectRatio, Qt.SmoothTransformation)
self.ui.custom_model_preview_label.setPixmap(scaled_pixmap)


def install_custom_model_zip(self):
try:
zip_path, selected_filter = QFileDialog.getOpenFileName(self, "Select custom model zip file", CUSTOM_MODELS_PATH, "Zip Files (*.zip)")
if not zip_path:
return
zip = zipfile.ZipFile(zip_path)
try:
top_level_dir = zipfile.Path(zip, zip.namelist()[0])
except IndexError:
QMessageBox.critical(
self, "Incorrect archive structure",
"Archive is empty"
)
return
# Verify contents
if top_level_dir.joinpath("models").is_dir():
model_path = top_level_dir.joinpath("models")
model_dir_list = list(model_path.iterdir())
is_model_pack = True
else:
model_dir_list = [top_level_dir]
is_model_pack = False
expected_files = ["Link.arc", "metadata.txt"]
for model_dir in model_dir_list:
for f in expected_files:
if not model_dir.joinpath(f).exists():
QMessageBox.critical(
self, "Incorrect archive structure",
"Missing file: %s" % model_dir.joinpath(f).at
)
return
zip.extractall(CUSTOM_MODELS_PATH)
if not is_model_pack:
install_result = model_dir_list[0].name
else:
for model_dir in model_dir_list:
shutil.move(os.path.join(CUSTOM_MODELS_PATH, model_dir.at), os.path.join(CUSTOM_MODELS_PATH, model_dir.name))
shutil.rmtree(os.path.join(CUSTOM_MODELS_PATH, top_level_dir.name))
install_result = "%s models" % len(model_dir_list)
QMessageBox.information(
self, "Installation complete",
"%s installed successfully" % install_result
)
self.update_custom_player_model_list()
self.set_option_value("custom_player_model", model_dir_list[0].name)
except zipfile.BadZipfile as e:
stack_trace = traceback.format_exc()
print(stack_trace)
QMessageBox.critical(
self, "Failed to unpack model archive",
stack_trace
)

def open_about(self):
text = """Wind Waker Randomizer Version %s<br><br>
Created by LagoLunatic<br><br>
Expand Down
45 changes: 26 additions & 19 deletions wwr_ui/randomizer_window.ui
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>925</width>
<height>570</height>
<width>1097</width>
<height>783</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
Expand Down Expand Up @@ -870,21 +870,7 @@
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<layout class="QGridLayout" name="gridLayout_5">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_for_custom_player_model">
<property name="text">
<string>Player Model</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="custom_player_model"/>
</item>
</layout>
</item>
<item row="1" column="0">
<item row="2" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_12">
<item>
<widget class="QLabel" name="label_for_custom_color_preset">
Expand All @@ -898,7 +884,7 @@
</item>
</layout>
</item>
<item row="1" column="1">
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_11">
<item>
<widget class="QPushButton" name="randomize_all_custom_colors_together">
Expand All @@ -916,7 +902,21 @@
</item>
</layout>
</item>
<item row="0" column="1">
<item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_for_custom_player_model">
<property name="text">
<string>Player Model</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="custom_player_model"/>
</item>
</layout>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QCheckBox" name="player_in_casual_clothes">
Expand All @@ -941,6 +941,13 @@
</item>
</layout>
</item>
<item row="0" column="0" colspan="2">
<widget class="QPushButton" name="install_custom_model">
<property name="text">
<string>Install a Custom Model or Model Pack</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
Expand Down
Loading

0 comments on commit a0ae163

Please sign in to comment.