diff --git a/.github/workflows/build_android.yml b/.github/workflows/build_android.yml index fc7cd462..85450a3a 100644 --- a/.github/workflows/build_android.yml +++ b/.github/workflows/build_android.yml @@ -59,10 +59,6 @@ jobs: ref: ${{ inputs.firebase_cpp_sdk_version }} submodules: true - - uses: ruby/setup-ruby@v1 - with: - ruby-version: 2.6 - - name: Setup python uses: actions/setup-python@v4 with: diff --git a/.github/workflows/build_linux.yml b/.github/workflows/build_linux.yml index af21812e..677bb258 100644 --- a/.github/workflows/build_linux.yml +++ b/.github/workflows/build_linux.yml @@ -55,12 +55,6 @@ jobs: submodules: true ref: ${{ inputs.firebase_cpp_sdk_version }} - # Set up the requirements, and install Unity - # Ruby setup has to happen before python install and setup, to keep the absl config. - - uses: ruby/setup-ruby@v1 - with: - ruby-version: 3.0.2 - - name: Setup python uses: actions/setup-python@v4 with: diff --git a/.github/workflows/build_macos.yml b/.github/workflows/build_macos.yml index 4e9cd5b7..46712ee8 100644 --- a/.github/workflows/build_macos.yml +++ b/.github/workflows/build_macos.yml @@ -55,12 +55,6 @@ jobs: submodules: true ref: ${{ inputs.firebase_cpp_sdk_version }} - # Set up the requirements, and install Unity - # Ruby setup has to happen before python install and setup, to keep the absl config. - - uses: ruby/setup-ruby@v1 - with: - ruby-version: 3.0.2 - # Remove other python installs before installing the version we want - name: Remove other pythons shell: bash diff --git a/.github/workflows/build_windows.yml b/.github/workflows/build_windows.yml index f31485b8..f97a421d 100644 --- a/.github/workflows/build_windows.yml +++ b/.github/workflows/build_windows.yml @@ -58,12 +58,6 @@ jobs: - name: Support longpaths run: git config --system core.longpaths true - # Set up the requirements, and install Unity - # Ruby setup has to happen before python install and setup, to keep the absl config. - - uses: ruby/setup-ruby@v1 - with: - ruby-version: 3.0.2 - - name: Setup python uses: actions/setup-python@v4 with: diff --git a/gha/unity/README.md b/gha/unity/README.md index d8e1ee4e..d44878a2 100644 --- a/gha/unity/README.md +++ b/gha/unity/README.md @@ -3,24 +3,23 @@ ## Inputs - `version`: **[Required]** Unity Major Version Number. Currently supported values: [2019, 2020]. -- `platforms`: Platforms that you'd like to support, if not provided, some platforms may encounter errors. Values: [Android,iOS,tvOS,Windows,macOS,Linux]. +- `platforms`: Platforms that you'd like to support, if not provided, build apps on certain platforms may encounter errors. Values: [Android,iOS,tvOS,Windows,macOS,Linux]. -- `username`: Required when Activate Unity license. Refer to the Usage section below. +- `username`: Required when Activate Unity license. See the [Usage](https://github.com/firebase/firebase-unity-sdk/tree/main/gha/unity#usage) section below. -- `password`: Required when Activate Unity license. Refer to the Usage section below. +- `password`: Required when Activate Unity license. See the Usage section below. -- `serial_ids`: Required when Activate Unity license. Refer to the Usage section below. +- `serial_ids`: Required when Activate Unity license. See the Usage section below. -- `release_license`: If a license has been activated in the pervious step, then **must** add anothe step and set **"ture"** to `release_license` input. +- `release_license`: If a license has been activated in the pervious step, then **must** add another step to release the license. Set **"ture"** to `release_license` input. ## Output -This GitHub Action will provide `UNITY_VERSION` (full unity version) and `UNITY_ROOT_DIR` (Unity project directory) environment variable +This GitHub Action will provide `UNITY_VERSION` (full unity version, e.g. `2020.3.34f1`) and `UNITY_ROOT_DIR` (Unity project directory, e.g. `/Applications/Unity/Hub/Editor/2020.3.34f1`) environment variables as outputs. - Output usage: ```yml - - id: unity_setup - uses: firebase/firebase-unity-sdk/gha/unity@main + - uses: firebase/firebase-unity-sdk/gha/unity@main with: version: ${{ unity_version }} platforms: ${{ platforms }} @@ -38,8 +37,7 @@ This GitHub Action will provide `UNITY_VERSION` (full unity version) and `UNITY_ steps: # ... - - id: unity_setup - uses: firebase/firebase-unity-sdk/gha/unity@main + - uses: firebase/firebase-unity-sdk/gha/unity@main with: version: ${{ unity_version }} platforms: ${{ platforms }} @@ -53,7 +51,7 @@ This GitHub Action will provide `UNITY_VERSION` (full unity version) and `UNITY_ steps: # ... - - id: unity_setup + - id: unity_setup_and_activate uses: firebase/firebase-unity-sdk/gha/unity@main with: version: ${{ unity_version }} @@ -69,29 +67,45 @@ This GitHub Action will provide `UNITY_VERSION` (full unity version) and `UNITY_ release_license: "true" ``` -## [Deprecated] How to upgrade supported unity versions +## How to upgrade supported unity versions **Background** -This GitHub Action leverages [U3D](github.com/DragonBox/u3d), which is a command line tool for working with Unity from the command line on all three operating systems. +This GitHub Action leverages [Unity Hub](https://unity3d.com/get-unity/download), which is a standalone application that streamlines the way you navigate, download, and manage your Unity projects and installations. Unity Hub is with beta version CLI support, and we are using it for Unity versions management. -In this GitHub Action, supported Unity Versions are maintained by `UNITY_SETTINGS` in `gha/unity/unity_installer.py`. +In this GitHub Action, supported Unity Versions are maintained by `SETTINGS` in `gha/unity/unity_installer.py`. **Add a new Unity version support** -1. Install [U3D](github.com/DragonBox/u3d). +1. Select your version from [Unity LTS versions list](https://unity3d.com/unity/qa/lts-releases) and make sure this version can be installed with Unity Hub. 2. Generate new JSON string and added it to `UNITY_SETTINGS`: - - `Major_version_number`: unity major version number: 2020, 2021, etc. - - `Full_version_number`: unity full version number. e.g. 2020.3.34f1 for major version 2020. Run `u3d available` and select [Unity LTS versions](https://unity3d.com/unity/qa/lts-releases). - - `Platform`: Values of [Android,iOS,tvOS,Windows,macOS,Linux] - - `Package`:[Unity Hub must **not** been installed] Unity Packages that required for certain platform. e.g. ["Windows-mono"] pakcages for "Windows" platform. To list avaliable packages, run `u3d available -u $unity_version -p`. + - `Major_version_number`: unity major version number: `2020`, `2021`, etc. + - `Full_version_number`: unity full version number. e.g. `2020.3.34f1` for major version `2020`. + - `Changeset`: changeset locates at the bottom of this page https://unity3d.com/unity/whats-new/{unity_version}. Note: the version is neither `Major_version_number` nor `Full_version_number`. e.g. https://unity3d.com/unity/whats-new/2020.3.34 + - `Platform`: Firebase Unity SDK supported platforms. Values of [Android,iOS,tvOS,Windows,macOS,Linux] + - `Modules`:[Unity Hub must been installed] Unity modules that required for certain platform. e.g. ["windows-mono"] module for "Windows" platform. To list avaliable modules, run `"/Applications/Unity Hub.app/Contents/MacOS/Unity Hub" -- --headless help` on your mac machine. + Template: ``` UNITY_SETTINGS = { Major_version_number: { OS: { "version": Full_version_number, - "packages": {Platform: [Package], ...}, + "changeset": Changeset, + "modules": {Platform: [Moudles], ...}, + }, + ... + }, + } + ``` + e.g. + ``` + UNITY_SETTINGS = { + "2020": { + WINDOWS: { + "version": "2020.3.34f1", + "changeset": "9a4c9c70452b", + "modules": {ANDROID: ["android", "ios"], IOS: ["ios"], TVOS: ["appletv"], WINDOWS: [], MACOS: ["mac-mono"], LINUX: ["linux-mono"], PLAYMODE: ["ios"]}, }, ... }, @@ -100,10 +114,4 @@ In this GitHub Action, supported Unity Versions are maintained by `UNITY_SETTING **Common failures & solutions** -1. If you met problem with `u3d` cmd (e.g. `u3d available -u $unity_version -p`), please install older version of `u3d` and disable the `u3d` version check. Then try it again. - ``` - gem install u3d -v 1.2.3 - export U3D_SKIP_UPDATE_CHECK=1 - ``` - -2. If you have problem with Android build. Make sure you are using the right version of NDK and JDK. Testapp building process is using a patch function `patch_android_env` in `build_testapp.py`. (Please refer [Unity Documentation](https://docs.unity3d.com/Manual/android-sdksetup.html) for Android environment setup). +1. If you have problem with Android build. Make sure you are using the right version of NDK and JDK. Testapp building process is using a patch function `patch_android_env` in `build_testapp.py`. (Please refer [Unity Documentation](https://docs.unity3d.com/Manual/android-sdksetup.html) for Android environment setup). diff --git a/gha/unity/action.yml b/gha/unity/action.yml index e51b17a8..0299b956 100644 --- a/gha/unity/action.yml +++ b/gha/unity/action.yml @@ -30,8 +30,19 @@ inputs: runs: using: 'composite' steps: + - id: configs + shell: bash + run: | + if [[ "${{ inputs.release_license }}" != "" ]]; then + echo "release_license=True" >> $GITHUB_OUTPUT + else + echo "install_unity=True" >> $GITHUB_OUTPUT + if [[ "${{ inputs.username }}" != "" && "${{ inputs.password }}" != "" && "${{ inputs.serial_ids }}" != "" ]]; then + echo "activate_license=True" >> $GITHUB_OUTPUT + fi + fi - name: Return Unity license - if: inputs.release_license + if: steps.configs.outputs.release_license uses: nick-invision/retry@v2 with: timeout_minutes: 5 @@ -42,28 +53,18 @@ runs: --version ${{ inputs.version }} \ --logfile "testapps/release_license.log" cat testapps/release_license.log - - name: unity_setting - if: inputs.release_license == '' - shell: bash - run: | - unity_info=$( python $GITHUB_ACTION_PATH/unity_installer.py --setting --version ${{ inputs.version }}) - echo "UNITY_VERSION=$(cut -d',' -f1 <<< ${unity_info})" >> $GITHUB_ENV - echo "UNITY_ROOT_DIR=$(cut -d',' -f2 <<< ${unity_info})" >> $GITHUB_ENV - name: Install Unity - if: inputs.release_license == '' - uses: kuler90/setup-unity@v1 - with: - unity-version: ${{ env.UNITY_VERSION }} - - name: Install Unity Modules - if: inputs.release_license == '' + if: steps.configs.outputs.install_unity shell: bash run: | if [[ -n "${{ inputs.platforms }}" ]]; then additional_flags+=(--platforms ${{ inputs.platforms }}) fi - python $GITHUB_ACTION_PATH/unity_installer.py --install_modules --version ${{ inputs.version }} ${additional_flags[*]} + unity_info=$(python $GITHUB_ACTION_PATH/unity_installer.py --install --version ${{ inputs.version }} ${additional_flags[*]}) + echo "UNITY_VERSION=$(cut -d',' -f1 <<< ${unity_info})" >> $GITHUB_ENV + echo "UNITY_ROOT_DIR=$(cut -d',' -f2 <<< ${unity_info})" >> $GITHUB_ENV - name: Activate Unity license - if: inputs.release_license == '' && inputs.username != '' && inputs.password != '' && inputs.serial_ids != '' + if: steps.configs.outputs.activate_license shell: bash run: | python $GITHUB_ACTION_PATH/unity_installer.py --activate_license \ diff --git a/gha/unity/unity_installer.py b/gha/unity/unity_installer.py index cad0a85b..c5c08518 100644 --- a/gha/unity/unity_installer.py +++ b/gha/unity/unity_installer.py @@ -27,15 +27,14 @@ TODO: (1) Installation: - unity_installer.py --install --version 2017.3.1f1 --platforms Android,iOS + unity_installer.py --install --version 2020 --platforms Android,iOS -'platforms' specifies additional build supports to install. Always installs -Unity itself. +'platforms' specifies additional build modules to install. (2) License activation: - unity_installer.py --activate_license --version 2017.3.1f1 \ + unity_installer.py --activate_license --version 2020 \ --license_file ~/license.txt --logfile activate.log or: @@ -57,78 +56,114 @@ (3) License release: - unity_installer.py --release_license --version 2019 --logfile return.log + unity_installer.py --release_license --version 2020 --logfile return.log """ +import requests import platform -import shutil import subprocess +import glob +import os from absl import app from absl import flags from absl import logging - - -_CMD_TIMEOUT = 900 -_MAX_ATTEMPTS = 3 - -_DEFALUT = "Default" -_ANDROID = "Android" -_IOS = "iOS" -_TVOS = "tvOS" -_WINDOWS = "Windows" -_MACOS = "macOS" -_LINUX = "Linux" -_SUPPORTED_PLATFORMS = (_ANDROID, _IOS, _TVOS, _WINDOWS, _MACOS, _LINUX) - -# Plese use Unity LTS versions: https://unity3d.com/unity/qa/lts-releases -# The modules below is valid only if Unity Hub is not installed. -UNITY_SETTINGS = { +from os import path + + +CMD_TIMEOUT = 900 +MAX_ATTEMPTS = 3 + +ANDROID = "Android" +IOS = "iOS" +TVOS = "tvOS" +WINDOWS = "Windows" +MACOS = "macOS" +LINUX = "Linux" +PLAYMODE = "Playmode" +BUILD_OS = (WINDOWS, MACOS, LINUX) +SUPPORTED_PLATFORMS = (ANDROID, IOS, TVOS, WINDOWS, MACOS, LINUX, PLAYMODE) +UNITY_VERSION_PLACEHOLDER = "unity_version_placeholder" + +SETTINGS = { + # Used for downloading Unity Hub + "unity_hub_url": { + WINDOWS: "https://public-cdn.cloud.unity3d.com/hub/prod/UnityHubSetup.exe", + MACOS: "https://public-cdn.cloud.unity3d.com/hub/prod/UnityHubSetup.dmg", + LINUX: "https://public-cdn.cloud.unity3d.com/hub/prod/UnityHub.AppImage", + }, + # Unity Hub will be installed at this location + "unity_hub_path": { + WINDOWS: '"C:/Program Files/Unity Hub/Unity Hub.exe"', + MACOS: '"/Applications/Unity Hub.app"', + LINUX: '"/home/runner/Unity Hub/UnityHub.AppImage"', + }, + "unity_hub_executable": { + WINDOWS: '"C:/Program Files/Unity Hub/Unity Hub.exe" -- --headless', + MACOS: '"/Applications/Unity Hub.app/Contents/MacOS/Unity Hub" -- --headless', + LINUX: 'xvfb-run --auto-servernum "/home/runner/Unity Hub/UnityHub.AppImage" --headless', + }, + # Unity will be installed at this location + "unity_path": { + WINDOWS: f'"C:/Program Files/Unity/Hub/Editor/{UNITY_VERSION_PLACEHOLDER}"', + MACOS: f'"/Applications/Unity/Hub/Editor/{UNITY_VERSION_PLACEHOLDER}"', + LINUX: f'"/home/runner/Unity/Hub/Editor/{UNITY_VERSION_PLACEHOLDER}"', + }, + "unity_executable": { + WINDOWS: f'"C:/Program Files/Unity/Hub/Editor/{UNITY_VERSION_PLACEHOLDER}/Editor/Unity.exe"', + MACOS: f'"/Applications/Unity/Hub/Editor/{UNITY_VERSION_PLACEHOLDER}/Unity.app/Contents/MacOS/Unity"', + LINUX: None # Linux is not yet supported. + }, + # Please use Unity Hub supported versions. + # Please also use Unity LTS versions: https://unity3d.com/unity/qa/lts-releases + # Changeset is required for each version. + # Please find out the changeset at this page https://unity3d.com/unity/whats-new/{unity_version}. + # e.g. https://unity3d.com/unity/whats-new/2020.3.34 + # The modules are required to build sepecific platforms. + # The modules are valid only if Unity Hub & Unity are installed. "2020": { - _WINDOWS: { + WINDOWS: { "version": "2020.3.34f1", - "modules": {"Default": ["Unity"], "Android": ["android", "ios"], "iOS": ["ios"], "tvOS": ["appletv"], "Windows": None, "macOS": ["mac-mono"], "Linux": ["linux-mono"], "Playmode": ["ios"]}, + "changeset": "9a4c9c70452b", + "modules": {ANDROID: ["android", "ios"], IOS: ["ios"], TVOS: ["appletv"], WINDOWS: [], MACOS: ["mac-mono"], LINUX: ["linux-mono"], PLAYMODE: ["ios"]}, }, - _MACOS: { + MACOS: { "version": "2020.3.34f1", - "modules": {"Default": ["Unity"], "Android": ["android"], "iOS": ["ios", "appletv"], "tvOS": ["appletv"], "Windows": ["windows-mono"], "macOS": ["ios"], "Linux": ["linux-mono"], "Playmode": None}, + "changeset": "9a4c9c70452b", + "modules": {ANDROID: ["android"], IOS: ["ios", "appletv"], TVOS: ["appletv"], WINDOWS: ["windows-mono"], MACOS: ["ios"], LINUX: ["linux-mono"], PLAYMODE: []}, }, - _LINUX: { + LINUX: { "version": "2020.3.40f1", - "modules": {"Default": ["Unity"], "Android": ["android"], "iOS": ["ios"], "tvOS": None, "Windows": ["windows-mono"], "macOS": ["mac-mono"], "Linux": None, "Playmode": None} + "changeset": "ba48d4efcef1", + "modules": {ANDROID: ["android"], IOS: ["ios"], TVOS: [], WINDOWS: ["windows-mono"], MACOS: ["mac-mono"], LINUX: [], PLAYMODE: []} } }, "2019": { - _WINDOWS: { + WINDOWS: { "version": "2019.4.39f1", - "modules": {"Default": ["Unity"], "Android": ["android"], "iOS": ["ios"], "tvOS": ["appletv"], "Windows": None, "macOS": ["mac-mono"], "Linux": ["linux-mono"], "Playmode": ["ios"]}, + "changeset": "78d14dfa024b", + "modules": {ANDROID: ["android"], IOS: ["ios"], TVOS: ["appletv"], WINDOWS: [], MACOS: ["mac-mono"], LINUX: ["linux-mono"], PLAYMODE: ["ios"]}, }, - _MACOS: { + MACOS: { "version": "2019.4.39f1", - "modules": {"Default": ["Unity"], "Android": ["android"], "iOS": ["ios"], "tvOS": ["appletv"], "Windows": ["windows-mono"], "macOS": ["ios"], "Linux": ["linux-mono"], "Playmode": None}, + "changeset": "78d14dfa024b", + "modules": {ANDROID: ["android"], IOS: ["ios"], TVOS: ["appletv"], WINDOWS: ["windows-mono"], MACOS: ["ios"], LINUX: ["linux-mono"], PLAYMODE: []}, }, - _LINUX: { + LINUX: { "version": "2019.4.40f1", - "modules": {"Default": ["Unity"], "Android": ["android"], "iOS": ["ios"], "tvOS": ["appletv"], "Windows": ["windows-mono"], "macOS": ["mac-mono"], "Linux": None, "Playmode": None} + "changeset": "ffc62b691db5", + "modules": {ANDROID: ["android"], IOS: ["ios"], TVOS: ["appletv"], WINDOWS: ["windows-mono"], MACOS: ["mac-mono"], LINUX: [], PLAYMODE: []} } }, } -FLAGS = flags.FLAGS - -flags.DEFINE_bool( - "setting", False, - "Print out detailed Unity Setting. Supply --version.") -# TODO: @sunmou -# flags.DEFINE_bool( -# "install", False, -# "Install Unity and build supports. Supply --version and --platforms.") +FLAGS = flags.FLAGS flags.DEFINE_bool( - "install_modules", False, - "Install Unity Modules and build supports. Supply --version and --platforms.") + "install", False, + "Install Unity and build supports. Supply --version and --platforms.") flags.DEFINE_bool( "activate_license", False, @@ -151,23 +186,21 @@ # Instead, this tool is written to expect the same format as build_testapps.py. # This keeps the CI workflow logic simple. flags.DEFINE_list( - "platforms", None, + "platforms", [], "(Optional) Additional modules to install based on platforms. Should be" " in the format of Unity build targets, i.e. the same format as taken by" " build_testapps.py. Invalid values will be ignored." - " Valid values: " + ",".join(_SUPPORTED_PLATFORMS)) + " Valid values: " + ",".join(SUPPORTED_PLATFORMS)) def main(argv): if len(argv) > 1: raise app.UsageError("Too many command-line arguments.") - if FLAGS.setting: + if FLAGS.install: + install(FLAGS.version, FLAGS.platforms) print_setting(FLAGS.version) - if FLAGS.install_modules: - install_modules(FLAGS.version, FLAGS.platforms) - if FLAGS.activate_license: if FLAGS.license_file: with open(FLAGS.license_file, "r") as f: @@ -186,27 +219,73 @@ def main(argv): def print_setting(unity_version): - os = get_os() - unity_full_version = UNITY_SETTINGS[unity_version][os]["version"] - unity_path = get_unity_path(unity_version) + runner_os = get_os() + unity_full_version = SETTINGS[unity_version][runner_os]["version"] + unity_path = SETTINGS["unity_path"][runner_os].replace(UNITY_VERSION_PLACEHOLDER, unity_full_version) print("%s,%s" % (unity_full_version, unity_path)) -def install_modules(unity_version, platforms): - os = get_os() - unity_full_version = UNITY_SETTINGS[unity_version][os]["version"] - unity_hub_path = get_unity_hub_path() - if platforms: - for p in platforms: - if UNITY_SETTINGS[unity_version][os]["modules"][p]: - for module in UNITY_SETTINGS[unity_version][os]["modules"][p]: - run([unity_hub_path, "--", "--headless", - "install-modules", - "--version", unity_full_version, - "--module", module, - "--childModules"], - check=False) +def install(unity_version, platforms): + install_unity_hub() + + runner_os = get_os() + unity_full_version = SETTINGS[unity_version][runner_os]["version"] + changeset = SETTINGS[unity_version][runner_os]["changeset"] + install_unity(unity_full_version, changeset) + + for p in platforms: + for module in SETTINGS[unity_version][runner_os]["modules"][p]: + install_module(unity_full_version, module) + + +def install_unity_hub(): + runner_os = get_os() + unity_hub_url = SETTINGS["unity_hub_url"][runner_os] + unity_hub_installer = path.basename(unity_hub_url) + download_unity_hub(unity_hub_url, unity_hub_installer, max_attempts=MAX_ATTEMPTS) + if runner_os == MACOS: + run(f'sudo hdiutil attach {unity_hub_installer}', max_attempts=MAX_ATTEMPTS) + mounted_to = glob.glob("/Volumes/Unity Hub*/Unity Hub.app") + if mounted_to: + run(f'sudo cp -R "{mounted_to[0]}" "/Applications"', max_attempts=MAX_ATTEMPTS) + run('sudo mkdir -p "/Library/Application Support/Unity"') + run(f'sudo chown -R {os.environ["USER"]} "/Library/Application Support/Unity"') + elif runner_os == WINDOWS: + run(f'{unity_hub_installer} /S', max_attempts=MAX_ATTEMPTS) + elif runner_os == LINUX: + home_dir = os.environ["HOME"] + unity_hub_path = SETTINGS["unity_hub_path"][runner_os] + run(f'mkdir -p "{home_dir}/Unity Hub" "{home_dir}/.config/Unity Hub"') + run(f'mv {unity_hub_installer} {unity_hub_path}') + run(f'chmod +x {unity_hub_path}') + run(f'touch "{home_dir}/.config/Unity Hub/eulaAccepted"', max_attempts=MAX_ATTEMPTS) + + +def download_unity_hub(unity_hub_url, unity_hub_installer, max_attempts=1): + attempt_num = 1 + while attempt_num <= max_attempts: + try: + response = requests.get(unity_hub_url) + open(unity_hub_installer, "wb").write(response.content) + except Exception as e: + logging.info("download unity hub failed. URL: %s (attempt %s of %s). Exception: %s", Exception, e) + if attempt_num >= max_attempts: + raise + else: + break + attempt_num += 1 + + +def install_unity(unity_full_version, changeset): + unity_hub_executable = SETTINGS["unity_hub_executable"][get_os()] + run(f'{unity_hub_executable} install --version {unity_full_version} --changeset {changeset}', max_attempts=MAX_ATTEMPTS) + run(f'{unity_hub_executable} editors --installed') + +def install_module(unity_full_version, module): + unity_hub_executable = SETTINGS["unity_hub_executable"][get_os()] + run(f'{unity_hub_executable} install-modules --version {unity_full_version} --module {module} --childModules', max_attempts=MAX_ATTEMPTS) + def activate_license(username, password, serial_ids, logfile, unity_version): """Activates an installation of Unity with a license.""" @@ -214,16 +293,13 @@ def activate_license(username, password, serial_ids, logfile, unity_version): # succeeds. This has occurred e.g. in Unity 2019.3.15 on Mac. # To handle this case, we check the Unity logs for the message indicating # successful activation and ignore the error in that case. - unity = get_unity_executable(unity_version) + unity_full_version = SETTINGS[unity_version][get_os()]["version"] + unity_executable = SETTINGS["unity_executable"][get_os()].replace(UNITY_VERSION_PLACEHOLDER, unity_full_version) logging.info("Found %d licenses. Attempting each.", len(serial_ids)) for i, serial_id in enumerate(serial_ids): logging.info("Attempting license %d", i) try: - run([unity, "-quit", "-batchmode", - "-username", username, - "-password", password, - "-serial", serial_id, - "-logfile", logfile]) + run(f'{unity_executable} -quit -batchmode -username {username} -password {password} -serial {serial_id} -logfile {logfile}') logging.info("Activated Unity license.") return except subprocess.CalledProcessError as e: @@ -245,60 +321,40 @@ def activate_license(username, password, serial_ids, logfile, unity_version): def release_license(logfile, unity_version): """Releases the Unity license. Requires finding an installation of Unity.""" - unity = get_unity_executable(unity_version) - run([unity, "-quit", "-batchmode", "-returnlicense", "-logfile", logfile]) + unity_full_version = SETTINGS[unity_version][get_os()]["version"] + unity_executable = SETTINGS["unity_executable"][get_os()].replace(UNITY_VERSION_PLACEHOLDER, unity_full_version) + run(f'{unity_executable} -quit -batchmode -returnlicense -logfile {logfile}') logging.info("Unity license released.") def get_os(): """Current Operation System""" if platform.system() == 'Windows': - return _WINDOWS + return WINDOWS elif platform.system() == 'Darwin': - return _MACOS - elif platform.system() == 'Linux': - return _LINUX - - -def get_unity_executable(version): - """Returns the path to this version of Unity.""" - full_version = UNITY_SETTINGS[version][get_os()]["version"] - if platform.system() == "Windows": - return "C:/Program Files/Unity/Hub/Editor/%s/Editor/Unity.exe" % full_version - elif platform.system() == "Darwin": - return "/Applications/Unity/Hub/Editor/%s/Unity.app/Contents/MacOS/Unity" % full_version - else: - # Linux is not yet supported. - raise RuntimeError("Only Windows and MacOS are supported.") - - -def get_unity_hub_path(): - """Returns the path to Unity Hub.""" - if platform.system() == "Windows": - return "C:/Program Files/Unity Hub/Unity Hub.exe" - elif platform.system() == "Darwin": - return "/Applications/Unity Hub.app/Contents/MacOS/Unity Hub" + return MACOS elif platform.system() == 'Linux': - return "/home/runner/Unity Hub/UnityHub.AppImage" - - -def get_unity_path(version): - """Returns the path to this version of Unity.""" - full_version = UNITY_SETTINGS[version][get_os()]["version"] - if platform.system() == "Windows": - return "C:/Program Files/Unity/Hub/Editor/%s" % full_version - elif platform.system() == "Darwin": - return "/Applications/Unity/Hub/Editor/%s" % full_version - elif platform.system() == 'Linux': - return "/home/runner/Unity/Hub/Editor/%s" % full_version + return LINUX -def run(args, check=True, timeout=_CMD_TIMEOUT): +def run(command, check=True, max_attempts=1): """Runs args in a subprocess, throwing an error on non-zero return code.""" - logging.info("run cmd: %s", " ".join(args)) - result = subprocess.run(args=args, check=check, timeout=timeout, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) - logging.info("cmd result.stdout: %s", result.stdout) - logging.info("cmd result.stderr: %s", result.stderr) + attempt_num = 1 + while attempt_num <= max_attempts: + try: + logging.info("run_with_retry: %s (attempt %s of %s)", command, attempt_num, max_attempts) + result = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=True) + if result.stdout: + logging.info("cmd stdout: %s", result.stdout.read().strip()) + if result.stderr: + logging.info("cmd stderr: %s", result.stderr.read().strip()) + except subprocess.SubprocessError as e: + logging.exception("run_with_retry: %s (attempt %s of %s) FAILED: %s", command, attempt_num, max_attempts, e) + if check and (attempt_num >= max_attempts): + raise + else: + break + attempt_num += 1 if __name__ == "__main__":