From da62fe98baf03696877a301e1c48a1ff788f37ad Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Mon, 15 Mar 2021 22:15:19 -0700 Subject: [PATCH 1/9] Update build instructions --- docs/BUILDING.md | 18 ++++++++++-------- docs/images/cli-windows.png | Bin 0 -> 5686 bytes docs/images/cmd.png | Bin 0 -> 22846 bytes docs/images/py-dungeonrandomizer.png | Bin 0 -> 5404 bytes docs/images/py-gui.png | Bin 0 -> 12055 bytes docs/images/python.png | Bin 0 -> 20033 bytes docs/images/sourcecode.png | Bin 0 -> 9701 bytes 7 files changed, 10 insertions(+), 8 deletions(-) create mode 100644 docs/images/cli-windows.png create mode 100644 docs/images/cmd.png create mode 100644 docs/images/py-dungeonrandomizer.png create mode 100644 docs/images/py-gui.png create mode 100644 docs/images/python.png create mode 100644 docs/images/sourcecode.png diff --git a/docs/BUILDING.md b/docs/BUILDING.md index fd0233f0..461601aa 100644 --- a/docs/BUILDING.md +++ b/docs/BUILDING.md @@ -1,13 +1,15 @@ # Running from source -1. Get [python](http://python.org/downloads) -1. Get the [Door Randomizer Unstable source code](https://github.com/Aerinon/ALttPDoorRandomizer/archive/DoorDevUnstable.zip) -1. Install Platform-specific dependencies -1. Run `DoorRandomizer.py` for command-line script -1. Run `Gui.py` for user interface +|Instruction|Image| +|-----------|-----| +|Get [python](http://python.org/downloads)|[Get python](images/python.png) +|Get the [Door Randomizer Unstable source code](https://github.com/Aerinon/ALttPDoorRandomizer/archive/DoorDevUnstable.zip)|[Get source code](images/sourcecode.png) +|Install Platform-specific dependencies (see below)|[Command line](images/cmd.png) +|Run `DoorRandomizer.py` for command-line script|[DungeonRandomizer.py](images/py-dungeonrandomizer.png) +|Run `Gui.py` for user interface|[Gui.py](images/py-gui.png) ## Platform-specific dependencies -### Windows - -* Run `resources/ci/common/local_install.py` +|Platform|Command line|Image| +| :----: |------------|-----| +|Windows |`resources/ci/common/local_install.py`|[Windows](images/cli-windows.png) diff --git a/docs/images/cli-windows.png b/docs/images/cli-windows.png new file mode 100644 index 0000000000000000000000000000000000000000..2cb7a092ea9bb2e98b1184d1d3087d491ef9a19a GIT binary patch literal 5686 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 literal 0 HcmV?d00001 diff --git a/docs/images/cmd.png b/docs/images/cmd.png new file mode 100644 index 0000000000000000000000000000000000000000..cb2b6973fecf6e23e730929b5b5c53c0acead60c GIT binary patch literal 22846 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 literal 0 HcmV?d00001 diff --git a/docs/images/py-dungeonrandomizer.png b/docs/images/py-dungeonrandomizer.png new file mode 100644 index 0000000000000000000000000000000000000000..f3d8de6ad5246062dc963ab9e8c07701f339a89c GIT binary patch literal 5404 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 a)pebD&A?2!75dNZVjT?w^=eh8xc>$DPRW!2 literal 0 HcmV?d00001 diff --git a/docs/images/python.png b/docs/images/python.png new file mode 100644 index 0000000000000000000000000000000000000000..87bd1a61adfa20c3e25f6e46ab8918336b5954b5 GIT binary patch literal 20033 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 literal 0 HcmV?d00001 From 3a370eb9f9cffc4bddfb79c24b892d1c838ca3cf Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Mon, 15 Mar 2021 22:17:33 -0700 Subject: [PATCH 2/9] Update BUILDING.md --- docs/BUILDING.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/BUILDING.md b/docs/BUILDING.md index 461601aa..8102369c 100644 --- a/docs/BUILDING.md +++ b/docs/BUILDING.md @@ -2,14 +2,14 @@ |Instruction|Image| |-----------|-----| -|Get [python](http://python.org/downloads)|[Get python](images/python.png) -|Get the [Door Randomizer Unstable source code](https://github.com/Aerinon/ALttPDoorRandomizer/archive/DoorDevUnstable.zip)|[Get source code](images/sourcecode.png) -|Install Platform-specific dependencies (see below)|[Command line](images/cmd.png) -|Run `DoorRandomizer.py` for command-line script|[DungeonRandomizer.py](images/py-dungeonrandomizer.png) -|Run `Gui.py` for user interface|[Gui.py](images/py-gui.png) +|Get [python](http://python.org/downloads)|[Get python](https://raw.githubusercontent.com/miketrethewey/ALttPDoorRandomizer/DoorDevUnstable/docs/images/python.png) +|Get the [Door Randomizer Unstable source code](https://github.com/Aerinon/ALttPDoorRandomizer/archive/DoorDevUnstable.zip)|[Get source code](https://raw.githubusercontent.com/miketrethewey/ALttPDoorRandomizer/DoorDevUnstable/docs/images/sourcecode.png) +|Install Platform-specific dependencies (see below)|[Command line](https://raw.githubusercontent.com/miketrethewey/ALttPDoorRandomizer/DoorDevUnstable/docs/images/cmd.png) +|Run `DoorRandomizer.py` for command-line script|[DungeonRandomizer.py](https://raw.githubusercontent.com/miketrethewey/ALttPDoorRandomizer/DoorDevUnstable/docs/images/py-dungeonrandomizer.png) +|Run `Gui.py` for user interface|[Gui.py](https://raw.githubusercontent.com/miketrethewey/ALttPDoorRandomizer/DoorDevUnstable/docs/images/py-gui.png) ## Platform-specific dependencies |Platform|Command line|Image| | :----: |------------|-----| -|Windows |`resources/ci/common/local_install.py`|[Windows](images/cli-windows.png) +|Windows |`resources/ci/common/local_install.py`|[Windows](https://raw.githubusercontent.com/miketrethewey/ALttPDoorRandomizer/DoorDevUnstable/docs/images/cli-windows.png) From f769ada8eb86760ffea5c6732c93ca67b7d5b7b5 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Mon, 15 Mar 2021 22:19:28 -0700 Subject: [PATCH 3/9] Markdown is hard --- docs/BUILDING.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/BUILDING.md b/docs/BUILDING.md index 8102369c..27484f6b 100644 --- a/docs/BUILDING.md +++ b/docs/BUILDING.md @@ -2,14 +2,14 @@ |Instruction|Image| |-----------|-----| -|Get [python](http://python.org/downloads)|[Get python](https://raw.githubusercontent.com/miketrethewey/ALttPDoorRandomizer/DoorDevUnstable/docs/images/python.png) -|Get the [Door Randomizer Unstable source code](https://github.com/Aerinon/ALttPDoorRandomizer/archive/DoorDevUnstable.zip)|[Get source code](https://raw.githubusercontent.com/miketrethewey/ALttPDoorRandomizer/DoorDevUnstable/docs/images/sourcecode.png) -|Install Platform-specific dependencies (see below)|[Command line](https://raw.githubusercontent.com/miketrethewey/ALttPDoorRandomizer/DoorDevUnstable/docs/images/cmd.png) -|Run `DoorRandomizer.py` for command-line script|[DungeonRandomizer.py](https://raw.githubusercontent.com/miketrethewey/ALttPDoorRandomizer/DoorDevUnstable/docs/images/py-dungeonrandomizer.png) -|Run `Gui.py` for user interface|[Gui.py](https://raw.githubusercontent.com/miketrethewey/ALttPDoorRandomizer/DoorDevUnstable/docs/images/py-gui.png) +|Get [python](http://python.org/downloads)|![Get python](https://raw.githubusercontent.com/miketrethewey/ALttPDoorRandomizer/DoorDevUnstable/docs/images/python.png) +|Get the [Door Randomizer Unstable source code](https://github.com/Aerinon/ALttPDoorRandomizer/archive/DoorDevUnstable.zip)|![Get source code](https://raw.githubusercontent.com/miketrethewey/ALttPDoorRandomizer/DoorDevUnstable/docs/images/sourcecode.png) +|Install Platform-specific dependencies (see below)|![Command line](https://raw.githubusercontent.com/miketrethewey/ALttPDoorRandomizer/DoorDevUnstable/docs/images/cmd.png) +|Run `DoorRandomizer.py` for command-line script|![DungeonRandomizer.py](https://raw.githubusercontent.com/miketrethewey/ALttPDoorRandomizer/DoorDevUnstable/docs/images/py-dungeonrandomizer.png) +|Run `Gui.py` for user interface|![Gui.py](https://raw.githubusercontent.com/miketrethewey/ALttPDoorRandomizer/DoorDevUnstable/docs/images/py-gui.png) ## Platform-specific dependencies |Platform|Command line|Image| | :----: |------------|-----| -|Windows |`resources/ci/common/local_install.py`|[Windows](https://raw.githubusercontent.com/miketrethewey/ALttPDoorRandomizer/DoorDevUnstable/docs/images/cli-windows.png) +|Windows |`resources/ci/common/local_install.py`|![Windows](https://raw.githubusercontent.com/miketrethewey/ALttPDoorRandomizer/DoorDevUnstable/docs/images/cli-windows.png) From 61980e859df4635d9cc85fd9700fe295c4d8e7c1 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Mon, 15 Mar 2021 22:22:59 -0700 Subject: [PATCH 4/9] What's a script name? --- docs/BUILDING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/BUILDING.md b/docs/BUILDING.md index 27484f6b..965d07f1 100644 --- a/docs/BUILDING.md +++ b/docs/BUILDING.md @@ -5,7 +5,7 @@ |Get [python](http://python.org/downloads)|![Get python](https://raw.githubusercontent.com/miketrethewey/ALttPDoorRandomizer/DoorDevUnstable/docs/images/python.png) |Get the [Door Randomizer Unstable source code](https://github.com/Aerinon/ALttPDoorRandomizer/archive/DoorDevUnstable.zip)|![Get source code](https://raw.githubusercontent.com/miketrethewey/ALttPDoorRandomizer/DoorDevUnstable/docs/images/sourcecode.png) |Install Platform-specific dependencies (see below)|![Command line](https://raw.githubusercontent.com/miketrethewey/ALttPDoorRandomizer/DoorDevUnstable/docs/images/cmd.png) -|Run `DoorRandomizer.py` for command-line script|![DungeonRandomizer.py](https://raw.githubusercontent.com/miketrethewey/ALttPDoorRandomizer/DoorDevUnstable/docs/images/py-dungeonrandomizer.png) +|Run `DungeonRandomizer.py` for command-line script|![DungeonRandomizer.py](https://raw.githubusercontent.com/miketrethewey/ALttPDoorRandomizer/DoorDevUnstable/docs/images/py-dungeonrandomizer.png) |Run `Gui.py` for user interface|![Gui.py](https://raw.githubusercontent.com/miketrethewey/ALttPDoorRandomizer/DoorDevUnstable/docs/images/py-gui.png) ## Platform-specific dependencies From ed2813c85ed6a4409fbb92750eb9e78ac24fac48 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Tue, 16 Mar 2021 00:58:09 -0700 Subject: [PATCH 5/9] Make local install a little smarter --- docs/BUILDING.md | 4 ++ resources/ci/common/common.py | 12 +++++- resources/ci/common/get_get_pip.py | 31 +++++++++++--- resources/ci/common/install.py | 61 +++++++++++++++++++++++++--- resources/ci/common/local_install.py | 13 +++++- 5 files changed, 107 insertions(+), 14 deletions(-) diff --git a/docs/BUILDING.md b/docs/BUILDING.md index 965d07f1..298230ed 100644 --- a/docs/BUILDING.md +++ b/docs/BUILDING.md @@ -13,3 +13,7 @@ |Platform|Command line|Image| | :----: |------------|-----| |Windows |`resources/ci/common/local_install.py`|![Windows](https://raw.githubusercontent.com/miketrethewey/ALttPDoorRandomizer/DoorDevUnstable/docs/images/cli-windows.png) +|`py` Launcher: 3.9 |`resources/ci/common/local_install.py --py 3.9`| +|`py` Launcher: 3.8 |`resources/ci/common/local_install.py --py 3.8`| +|`py` Launcher: 3.7 |`resources/ci/common/local_install.py --py 3.7`| +|`py` Launcher: 3.6 |`resources/ci/common/local_install.py --py 3.6`| diff --git a/resources/ci/common/common.py b/resources/ci/common/common.py index f2288fe2..0dac9fb2 100644 --- a/resources/ci/common/common.py +++ b/resources/ci/common/common.py @@ -1,5 +1,6 @@ import os # for env vars import stat # file statistics +import sys # default system info global UBUNTU_VERSIONS global DEFAULT_EVENT @@ -75,10 +76,19 @@ def prepare_env(): env["BUILD_NUMBER"] = os.getenv("TRAVIS_BUILD_NUMBER",env["GITHUB_RUN_NUMBER"]) GITHUB_TAG = os.getenv("TRAVIS_TAG",os.getenv("GITHUB_TAG","")) - OS_NAME = os.getenv("TRAVIS_OS_NAME",os.getenv("OS_NAME","")).replace("macOS","osx") + OS_NAME = os.getenv("TRAVIS_OS_NAME",os.getenv("OS_NAME",sys.platform)).replace("macOS","osx") OS_DIST = os.getenv("TRAVIS_DIST","notset") OS_VERSION = "" + if "win32" in OS_NAME or \ + "cygwin" in OS_NAME or \ + "msys" in OS_NAME: + OS_NAME = "windows" + elif "darwin" in OS_NAME: + OS_NAME = "osx" + elif "linux2" in OS_NAME: + OS_NAME = "linux" + if '-' in OS_NAME: OS_VERSION = OS_NAME[OS_NAME.find('-')+1:] OS_NAME = OS_NAME[:OS_NAME.find('-')] diff --git a/resources/ci/common/get_get_pip.py b/resources/ci/common/get_get_pip.py index 9b69930d..4724ebd7 100644 --- a/resources/ci/common/get_get_pip.py +++ b/resources/ci/common/get_get_pip.py @@ -1,10 +1,14 @@ import common +import argparse import urllib.request, ssl import subprocess # do stuff at the shell level env = common.prepare_env() -def get_get_pip(): +def get_get_pip(PY_VERSION): + try: + import pip + except ImportError: print("Getting pip getter!") #make the request! url = "https://bootstrap.pypa.io/get-pip.py" @@ -29,11 +33,26 @@ def get_get_pip(): # linux/windows: python # macosx: python3 PYTHON_EXECUTABLE = "python3" if "osx" in env["OS_NAME"] else "python" + if PY_VERSION == None: + PY_VERSION = 0 + + if float(PY_VERSION) > 0: + PYTHON_EXECUTABLE = "py" + print("Getting pip!") - subprocess.check_call([PYTHON_EXECUTABLE,"get-pip.py"]) + args = [ + PYTHON_EXECUTABLE, + '-' + str(PY_VERSION), + "get-pip.py" + ] + if PY_VERSION == 0: + del args[1] + subprocess.check_call(args) if __name__ == "__main__": - try: - import pip - except ImportError: - get_get_pip() + parser = argparse.ArgumentParser(add_help=False) + parser.add_argument('--py', default=0) + command_line_args = parser.parse_args() + PY_VERSION = vars(command_line_args)["py"] + + get_get_pip(PY_VERSION) diff --git a/resources/ci/common/install.py b/resources/ci/common/install.py index 70d10202..f32b4866 100644 --- a/resources/ci/common/install.py +++ b/resources/ci/common/install.py @@ -1,9 +1,10 @@ import common +import argparse import subprocess # do stuff at the shell level env = common.prepare_env() -def run_install(): +def run_install(PY_VERSION,USER): # get executables # python # linux/windows: python @@ -15,16 +16,66 @@ def run_install(): PIP_EXECUTABLE = "pip" if "windows" in env["OS_NAME"] else "pip3" PIP_EXECUTABLE = "pip" if "osx" in env["OS_NAME"] and "actions" in env["CI_SYSTEM"] else PIP_EXECUTABLE + if PY_VERSION == None: + PY_VERSION = 0 + if USER == None: + USER = False + + if float(PY_VERSION) > 0: + PYTHON_EXECUTABLE = "py" + print("Installing to Python %.1f" % float(PY_VERSION)) + if USER: + print("Installing packages at User level") + # upgrade pip - subprocess.check_call([PYTHON_EXECUTABLE,"-m","pip","install","--upgrade","pip"]) + args = [ + PYTHON_EXECUTABLE, + '-' + str(PY_VERSION), + "-m", + "pip", + "install", + "--upgrade", + "--user", + "pip" + ] + if not USER: + args.remove("--user") + if PY_VERSION == 0: + del args[1] + subprocess.check_call(args) # pip version subprocess.check_call([PIP_EXECUTABLE,"--version"]) # if pip3, install wheel if PIP_EXECUTABLE == "pip3": - subprocess.check_call([PIP_EXECUTABLE,"install","-U","wheel"]) + args = [ + PIP_EXECUTABLE, + "install", + "--user", + "-U", + "wheel" + ] + if not USER: + args.remove("--user") + subprocess.check_call(args) # install listed dependencies - subprocess.check_call([PIP_EXECUTABLE,"install","-r","./resources/app/meta/manifests/pip_requirements.txt"]) + args = [ + PIP_EXECUTABLE, + "install", + "--user", + "-r", + "./resources/app/meta/manifests/pip_requirements.txt" + ] + if not USER: + args.remove("--user") + subprocess.check_call(args) if __name__ == "__main__": - run_install() + parser = argparse.ArgumentParser(add_help=False) + parser.add_argument('--py', default=0) + parser.add_argument('--user', default=False, action="store_true") + command_line_args = parser.parse_args() + PY_VERSION = vars(command_line_args)["py"] + USER = vars(command_line_args)["user"] + + run_install(PY_VERSION,USER) diff --git a/resources/ci/common/local_install.py b/resources/ci/common/local_install.py index 8cde8348..02cc25a5 100644 --- a/resources/ci/common/local_install.py +++ b/resources/ci/common/local_install.py @@ -1,8 +1,17 @@ import install import get_get_pip +import argparse + +parser = argparse.ArgumentParser(add_help=False) +parser.add_argument('--py', default=0) +parser.add_argument('--user', default=False, action="store_true") +command_line_args = parser.parse_args() +PY_VERSION = vars(command_line_args)["py"] +USER = vars(command_line_args)["user"] + # get & install pip -get_get_pip.get_get_pip() +get_get_pip.get_get_pip(PY_VERSION) # run installer -install.run_install() +install.run_install(PY_VERSION,USER) From d6a433e4671f4e376b75653ff2c59e9fc39d7412 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Wed, 17 Mar 2021 17:50:51 -0700 Subject: [PATCH 6/9] Add pathing intelligence --- resources/ci/common/common.py | 3 ++ resources/ci/common/get_get_pip.py | 83 ++++++++++++++++-------------- resources/ci/common/install.py | 27 +++++++--- resources/ci/common/my_path.py | 33 ++++++++++++ 4 files changed, 99 insertions(+), 47 deletions(-) create mode 100644 resources/ci/common/my_path.py diff --git a/resources/ci/common/common.py b/resources/ci/common/common.py index 0dac9fb2..b89a266b 100644 --- a/resources/ci/common/common.py +++ b/resources/ci/common/common.py @@ -1,6 +1,7 @@ import os # for env vars import stat # file statistics import sys # default system info +from my_path import get_py_path global UBUNTU_VERSIONS global DEFAULT_EVENT @@ -45,6 +46,8 @@ def prepare_env(): APP_VERSION = f.readlines()[0].strip() # ci data env["CI_SYSTEM"] = os.getenv("CI_SYSTEM","") + # py data + (env["PYTHON_EXE_PATH"],env["PY_EXE_PATH"],env["PIP_EXE_PATH"]) = get_py_path() # git data env["BRANCH"] = os.getenv("TRAVIS_BRANCH","") env["GITHUB_ACTOR"] = os.getenv("GITHUB_ACTOR","MegaMan.EXE") diff --git a/resources/ci/common/get_get_pip.py b/resources/ci/common/get_get_pip.py index 4724ebd7..a0e127ba 100644 --- a/resources/ci/common/get_get_pip.py +++ b/resources/ci/common/get_get_pip.py @@ -1,53 +1,54 @@ import common import argparse +import os import urllib.request, ssl import subprocess # do stuff at the shell level env = common.prepare_env() def get_get_pip(PY_VERSION): - try: - import pip - except ImportError: - print("Getting pip getter!") - #make the request! - url = "https://bootstrap.pypa.io/get-pip.py" - context = ssl._create_unverified_context() - req = urllib.request.urlopen(url, context=context) - got_pip = req.read().decode("utf-8") + try: + import pip + except ImportError: + print("Getting pip getter!") + #make the request! + url = "https://bootstrap.pypa.io/get-pip.py" + context = ssl._create_unverified_context() + req = urllib.request.urlopen(url, context=context) + got_pip = req.read().decode("utf-8") - with open("get-pip.py", "w") as g: - req = urllib.request.Request( - url, - data=None, - headers={ - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36" - } - ) - req = urllib.request.urlopen(req, context=context) - data = req.read().decode("utf-8") - g.write(data) + with open("get-pip.py", "w") as g: + req = urllib.request.Request( + url, + data=None, + headers={ + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36" + } + ) + req = urllib.request.urlopen(req, context=context) + data = req.read().decode("utf-8") + g.write(data) - # get executables - # python - # linux/windows: python - # macosx: python3 - PYTHON_EXECUTABLE = "python3" if "osx" in env["OS_NAME"] else "python" - if PY_VERSION == None: - PY_VERSION = 0 + # get executables + # python + # linux/windows: python + # macosx: python3 + PYTHON_EXECUTABLE = "python3" if "osx" in env["OS_NAME"] else "python" + if PY_VERSION == None: + PY_VERSION = 0 - if float(PY_VERSION) > 0: - PYTHON_EXECUTABLE = "py" + if float(PY_VERSION) > 0: + PYTHON_EXECUTABLE = "py" - print("Getting pip!") - args = [ - PYTHON_EXECUTABLE, - '-' + str(PY_VERSION), - "get-pip.py" - ] - if PY_VERSION == 0: - del args[1] - subprocess.check_call(args) + print("Getting pip!") + args = [ + env["PYTHON_EXE_PATH"] + PYTHON_EXECUTABLE, + '-' + str(PY_VERSION), + "get-pip.py" + ] + if PY_VERSION == 0: + del args[1] + subprocess.check_call(args) if __name__ == "__main__": parser = argparse.ArgumentParser(add_help=False) @@ -55,4 +56,8 @@ if __name__ == "__main__": command_line_args = parser.parse_args() PY_VERSION = vars(command_line_args)["py"] - get_get_pip(PY_VERSION) + try: + import pip + print("pip is installed") + except ImportError: + get_get_pip(PY_VERSION) diff --git a/resources/ci/common/install.py b/resources/ci/common/install.py index f32b4866..0a541742 100644 --- a/resources/ci/common/install.py +++ b/resources/ci/common/install.py @@ -1,5 +1,7 @@ import common import argparse +import os +import platform import subprocess # do stuff at the shell level env = common.prepare_env() @@ -12,7 +14,9 @@ def run_install(PY_VERSION,USER): # pip # linux/macosx: pip3 # windows: pip + PYTHON_PATH = env["PYTHON_EXE_PATH"] PYTHON_EXECUTABLE = "python3" if "osx" in env["OS_NAME"] else "python" + PIP_PATH = env["PIP_EXE_PATH"] PIP_EXECUTABLE = "pip" if "windows" in env["OS_NAME"] else "pip3" PIP_EXECUTABLE = "pip" if "osx" in env["OS_NAME"] and "actions" in env["CI_SYSTEM"] else PIP_EXECUTABLE @@ -23,13 +27,17 @@ def run_install(PY_VERSION,USER): if float(PY_VERSION) > 0: PYTHON_EXECUTABLE = "py" - print("Installing to Python %.1f" % float(PY_VERSION)) - if USER: - print("Installing packages at User level") + PYTHON_PATH = env["PY_EXE_PATH"] + print("Installing to Python %.1f via Py Launcher" % float(PY_VERSION)) + else: + print("Installing to Python %s" % platform.python_version()) + print("Installing packages at %s level" % ("User" if USER else "Global")) + print() + print("Upgrading pip-") # upgrade pip args = [ - PYTHON_EXECUTABLE, + PYTHON_PATH + PYTHON_EXECUTABLE, '-' + str(PY_VERSION), "-m", "pip", @@ -44,12 +52,11 @@ def run_install(PY_VERSION,USER): del args[1] subprocess.check_call(args) - # pip version - subprocess.check_call([PIP_EXECUTABLE,"--version"]) # if pip3, install wheel if PIP_EXECUTABLE == "pip3": + print("Installing Wheel!") args = [ - PIP_EXECUTABLE, + PIP_PATH + PIP_EXECUTABLE, "install", "--user", "-U", @@ -58,9 +65,13 @@ def run_install(PY_VERSION,USER): if not USER: args.remove("--user") subprocess.check_call(args) + + print() # install listed dependencies + print("Installing dependencies") + print("-----------------------") args = [ - PIP_EXECUTABLE, + PIP_PATH + PIP_EXECUTABLE, "install", "--user", "-r", diff --git a/resources/ci/common/my_path.py b/resources/ci/common/my_path.py new file mode 100644 index 00000000..7d91f15e --- /dev/null +++ b/resources/ci/common/my_path.py @@ -0,0 +1,33 @@ +import os +import sys + +def get_py_path(): + user_paths = os.environ["PATH"].split(os.pathsep) + (python,py) = ("","") + + for path in user_paths: + parts = path.split(os.sep) + part = parts[len(parts) - 1].lower() + if ("python" in part) and ('.' not in part): + path.replace(os.sep,os.sep + os.sep) + if path not in user_paths: + py = path + + for path in sys.path: + parts = path.split(os.sep) + part = parts[len(parts) - 1].lower() + if ("python" in part) and ('.' not in part): + path.replace(os.sep,os.sep + os.sep) + if path not in user_paths: + python = path + + paths = ( + os.path.join(python,"") if python != "" else "", + os.path.join(py,"") if py != "" else "", + os.path.join(python,"Scripts","") if python != "" else "" + ) + # print(paths) + return paths + +if __name__ == "__main__": + get_py_path() From e48a69d9e925caffab3c58ed6f0c881c24a21745 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Wed, 17 Mar 2021 23:58:20 -0700 Subject: [PATCH 7/9] Extrapolate requirements file --- resources/ci/common/install.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/resources/ci/common/install.py b/resources/ci/common/install.py index 0a541742..5874d0dd 100644 --- a/resources/ci/common/install.py +++ b/resources/ci/common/install.py @@ -6,6 +6,8 @@ import subprocess # do stuff at the shell level env = common.prepare_env() +pip_requirements = os.path.join(".","resources","app","meta","manifests","pip_requirements.txt") + def run_install(PY_VERSION,USER): # get executables # python @@ -75,7 +77,7 @@ def run_install(PY_VERSION,USER): "install", "--user", "-r", - "./resources/app/meta/manifests/pip_requirements.txt" + pip_requirements ] if not USER: args.remove("--user") From b4c5346b9c68f51eab06cfaa71a39e3f738153dc Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Thu, 18 Mar 2021 00:32:25 -0700 Subject: [PATCH 8/9] Update CI --- .github/workflows/ci.yml | 135 ++++++++++++++++++++++++++++----------- 1 file changed, 96 insertions(+), 39 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b12176d7..6252173f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,15 +27,15 @@ jobs: strategy: matrix: os-name: [ ubuntu-latest, ubuntu-18.04, macOS-latest, windows-latest ] - python-version: [ 3.8 ] + python-version: [ 3.9 ] # needs: [ install-test ] steps: # checkout commit - name: Checkout commit - uses: actions/checkout@v1 + uses: actions/checkout@v2 # install python - name: Install python - uses: actions/setup-python@v1 + uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} architecture: "x64" @@ -48,6 +48,28 @@ jobs: run: | python ./resources/ci/common/install.py pip install pyinstaller + # get parent directory + - name: Get Repo Name + uses: mad9000/actions-find-and-replace-string@1 + id: repoName + with: + source: ${{ github.repository }} + find: '${{ github.repository_owner }}/' + replace: '' + - name: Get Parent Directory Path (!Windows) + uses: mad9000/actions-find-and-replace-string@1 + id: parentDirNotWin + with: + source: ${{ github.workspace }} + find: '${{ steps.repoName.outputs.value }}/${{ steps.repoName.outputs.value }}' + replace: ${{ steps.repoName.outputs.value }} + - name: Get Parent Directory Path (Windows) + uses: mad9000/actions-find-and-replace-string@1 + id: parentDir + with: + source: ${{ steps.parentDirNotWin.outputs.value }} + find: '${{ steps.repoName.outputs.value }}\${{ steps.repoName.outputs.value }}' + replace: ${{ steps.repoName.outputs.value }} # try to get UPX - name: Get UPX env: @@ -70,10 +92,10 @@ jobs: python ./resources/ci/common/prepare_binary.py # upload binary artifacts for later step - name: Upload Binary Artifacts - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v2 with: name: binaries-${{ matrix.os-name }} - path: ../artifact + path: ${{ steps.parentDir.outputs.value }}/artifact # Install & Preparing Release # Set up environment @@ -87,18 +109,18 @@ jobs: # os & python versions strategy: matrix: - # install/release on not xenial + # install/release on not bionic os-name: [ ubuntu-latest, ubuntu-18.04, macOS-latest, windows-latest ] - python-version: [ 3.8 ] + python-version: [ 3.9 ] needs: [ install-build ] steps: # checkout commit - name: Checkout commit - uses: actions/checkout@v1 + uses: actions/checkout@v2 # install python - name: Install Python - uses: actions/setup-python@v1 + uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} architecture: "x64" @@ -110,9 +132,31 @@ jobs: OS_NAME: ${{ matrix.os-name }} run: | python ./resources/ci/common/install.py + # get parent directory + - name: Get Repo Name + uses: mad9000/actions-find-and-replace-string@1 + id: repoName + with: + source: ${{ github.repository }} + find: '${{ github.repository_owner }}/' + replace: '' + - name: Get Parent Directory Path (!Windows) + uses: mad9000/actions-find-and-replace-string@1 + id: parentDirNotWin + with: + source: ${{ github.workspace }} + find: '${{ steps.repoName.outputs.value }}/${{ steps.repoName.outputs.value }}' + replace: ${{ steps.repoName.outputs.value }} + - name: Get Parent Directory Path (Windows) + uses: mad9000/actions-find-and-replace-string@1 + id: parentDir + with: + source: ${{ steps.parentDirNotWin.outputs.value }} + find: '${{ steps.repoName.outputs.value }}\${{ steps.repoName.outputs.value }}' + replace: ${{ steps.repoName.outputs.value }} # download binary artifact - name: Download Binary Artifact - uses: actions/download-artifact@v1 + uses: actions/download-artifact@v2 with: name: binaries-${{ matrix.os-name }} path: ./ @@ -126,22 +170,22 @@ jobs: python ./resources/ci/common/prepare_release.py # upload appversion artifact for later step - name: Upload AppVersion Artifact - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v2 with: name: appversion-${{ matrix.os-name }} path: ./resources/app/meta/manifests/app_version.txt # upload archive artifact for later step - name: Upload Archive Artifact - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@v2 with: name: archive-${{ matrix.os-name }} - path: ../deploy + path: ${{ steps.parentDir.outputs.value }}/deploy # Deploy to GitHub Releases # Release Name: ALttPDoorRandomizer v${GITHUB_TAG} # Release Body: Inline content of RELEASENOTES.md # Release Body: Fallback to URL to RELEASENOTES.md - # Release Files: ../deploy + # Release Files: ${{ steps.parentDir.outputs.value }}/deploy deploy-release: name: Deploy GHReleases runs-on: ${{ matrix.os-name }} @@ -150,42 +194,64 @@ jobs: # os & python versions strategy: matrix: - # release only on focal/bionic + # release only on focal os-name: [ ubuntu-latest ] - python-version: [ 3.8 ] + python-version: [ 3.9 ] needs: [ install-prepare-release ] steps: # checkout commit - name: Checkout commit - uses: actions/checkout@v1 + uses: actions/checkout@v2 + # get parent directory + - name: Get Repo Name + uses: mad9000/actions-find-and-replace-string@1 + id: repoName + with: + source: ${{ github.repository }} + find: '${{ github.repository_owner }}/' + replace: '' + - name: Get Parent Directory Path (!Windows) + uses: mad9000/actions-find-and-replace-string@1 + id: parentDirNotWin + with: + source: ${{ github.workspace }} + find: '${{ steps.repoName.outputs.value }}/${{ steps.repoName.outputs.value }}' + replace: ${{ steps.repoName.outputs.value }} + - name: Get Parent Directory Path (Windows) + uses: mad9000/actions-find-and-replace-string@1 + id: parentDir + with: + source: ${{ steps.parentDirNotWin.outputs.value }} + find: '${{ steps.repoName.outputs.value }}\${{ steps.repoName.outputs.value }}' + replace: ${{ steps.repoName.outputs.value }} - name: Install Dependencies via pip run: | python -m pip install pytz requests # download appversion artifact - name: Download AppVersion Artifact - uses: actions/download-artifact@v1 + uses: actions/download-artifact@v2 with: name: appversion-${{ matrix.os-name }} - path: ../build + path: ${{ steps.parentDir.outputs.value }}/build # download ubuntu archive artifact - name: Download Ubuntu Archive Artifact - uses: actions/download-artifact@v1 + uses: actions/download-artifact@v2 with: name: archive-ubuntu-latest - path: ../deploy/linux + path: ${{ steps.parentDir.outputs.value }}/deploy/linux # download macos archive artifact - name: Download MacOS Archive Artifact - uses: actions/download-artifact@v1 + uses: actions/download-artifact@v2 with: name: archive-macOS-latest - path: ../deploy/macos + path: ${{ steps.parentDir.outputs.value }}/deploy/macos # download windows archive artifact - name: Download Windows Archive Artifact - uses: actions/download-artifact@v1 + uses: actions/download-artifact@v2 with: name: archive-windows-latest - path: ../deploy/windows + path: ${{ steps.parentDir.outputs.value }}/deploy/windows # debug info - name: Debug Info id: debug_info @@ -199,32 +265,23 @@ jobs: RELEASE_NAME="ALttPDoorRandomizer ${GITHUB_TAG}" echo "Release Name: ${RELEASE_NAME}" echo "Git Tag: ${GITHUB_TAG}" - # read releasenotes - - name: Read RELEASENOTES - id: release_notes - run: | - body="$(cat RELEASENOTES.md)" - body="${body//'%'/'%25'}" - body="${body//$'\n'/'%0A'}" - body="${body//$'\r'/'%0D'}" - echo "::set-output name=body::$body" # create a pre/release - name: Create a Pre/Release id: create_release - uses: actions/create-release@master + uses: actions/create-release@v1.1.4 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: tag_name: v${{ steps.debug_info.outputs.github_tag }} release_name: ALttPDoorRandomizer v${{ steps.debug_info.outputs.github_tag }} - body: ${{ steps.release_notes.outputs.body }} + body_path: RELEASENOTES.md draft: true prerelease: true if: contains(github.ref, 'master') || contains(github.ref, 'stable') || contains(github.ref, 'dev') || contains(github.ref, 'DoorRelease') # upload linux archive asset - name: Upload Linux Archive Asset id: upload-linux-asset - uses: actions/upload-release-asset@v1.0.1 + uses: actions/upload-release-asset@v1.0.2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: @@ -236,7 +293,7 @@ jobs: # upload macos archive asset - name: Upload MacOS Archive Asset id: upload-macos-asset - uses: actions/upload-release-asset@v1.0.1 + uses: actions/upload-release-asset@v1.0.2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: @@ -248,7 +305,7 @@ jobs: # upload windows archive asset - name: Upload Windows Archive Asset id: upload-windows-asset - uses: actions/upload-release-asset@v1.0.1 + uses: actions/upload-release-asset@v1.0.2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: From 4c9fc3464b9878fc2f0a101ce1d0c82ad20962e1 Mon Sep 17 00:00:00 2001 From: "Mike A. Trethewey" Date: Thu, 18 Mar 2021 00:42:17 -0700 Subject: [PATCH 9/9] Ubuntu is on Focal now --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6252173f..261dc125 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -287,7 +287,7 @@ jobs: with: upload_url: ${{ steps.create_release.outputs.upload_url }} asset_path: ../deploy/linux/ALttPDoorRandomizer.tar.gz - asset_name: ALttPDoorRandomizer-${{ steps.debug_info.outputs.github_tag }}-linux-bionic.tar.gz + asset_name: ALttPDoorRandomizer-${{ steps.debug_info.outputs.github_tag }}-linux-focal.tar.gz asset_content_type: application/gzip if: contains(github.ref, 'master') || contains(github.ref, 'stable') || contains(github.ref, 'dev') || contains(github.ref, 'DoorRelease') # upload macos archive asset