Merge pull request #906 from Kidev/official_ux
Some checks failed
Check tox tests / Check packaging 📦 (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Publish Python 🐍 distributions 📦 to PyPI / Build package (push) Has been cancelled
Test on GH actions environment / test (binary, windows-latest, 3.13, 6.6.3) (push) Has been cancelled
Test on GH actions environment / test (standard, ubuntu-latest, 3.13, 6.5.3) (push) Has been cancelled
Test on GH actions environment / test (standard, ubuntu-latest, 3.13, 6.6.3) (push) Has been cancelled
Test on GH actions environment / test (standard, ubuntu-latest, 3.13, 6.8.0) (push) Has been cancelled
Test on GH actions environment / test (standard, ubuntu-latest, 3.13, 6.8.1) (push) Has been cancelled
Test on GH actions environment / test (standard, windows-latest, 3.13, 6.5.3) (push) Has been cancelled
Test on GH actions environment / test (standard, windows-latest, 3.13, 6.6.3) (push) Has been cancelled
Test on GH actions environment / test (standard, windows-latest, 3.13, 6.7.3) (push) Has been cancelled
Publish Python 🐍 distributions 📦 to PyPI / publish Python 🐍 distributions 📦 to PyPI (push) Has been cancelled

Add --use-official-installer, fix official installer download after update 4.9
This commit is contained in:
Hiroshi Miura
2025-03-27 21:39:48 +09:00
committed by GitHub
8 changed files with 238 additions and 137 deletions

View File

@@ -60,6 +60,7 @@ class QtPackageManager:
self.packages: List[QtPackageInfo] = [] self.packages: List[QtPackageInfo] = []
self.username = username self.username = username
self.password = password self.password = password
self.logger = getLogger("aqt.commercial")
def _get_cache_dir(self) -> Path: def _get_cache_dir(self) -> Path:
"""Create and return cache directory path.""" """Create and return cache directory path."""
@@ -141,6 +142,7 @@ class QtPackageManager:
cmd.append(base_package) cmd.append(base_package)
try: try:
self.logger.info(f"Running: {cmd}")
output = safely_run_save_output(cmd, Settings.qt_installer_timeout) output = safely_run_save_output(cmd, Settings.qt_installer_timeout)
# Handle both string and CompletedProcess outputs # Handle both string and CompletedProcess outputs
@@ -156,8 +158,7 @@ class QtPackageManager:
self._save_to_cache() self._save_to_cache()
else: else:
# Log the actual output for debugging # Log the actual output for debugging
logger = getLogger("aqt.helper") self.logger.debug(f"Installer output: {output_text}")
logger.debug(f"Installer output: {output_text}")
raise RuntimeError("Failed to find package information in installer output") raise RuntimeError("Failed to find package information in installer output")
except Exception as e: except Exception as e:
@@ -217,6 +218,7 @@ class CommercialInstaller:
override: Optional[List[str]] = None, override: Optional[List[str]] = None,
modules: Optional[List[str]] = None, modules: Optional[List[str]] = None,
no_unattended: bool = False, no_unattended: bool = False,
dry_run: bool = False,
): ):
self.override = override self.override = override
self.target = target self.target = target
@@ -227,6 +229,7 @@ class CommercialInstaller:
self.base_url = base_url self.base_url = base_url
self.modules = modules self.modules = modules
self.no_unattended = no_unattended self.no_unattended = no_unattended
self.dry_run = dry_run
# Extract credentials from override if present # Extract credentials from override if present
if override: if override:
@@ -330,13 +333,12 @@ class CommercialInstaller:
raise DiskAccessNotPermitted(f"Failed to remove existing version directory {version_dir}: {str(e)}") raise DiskAccessNotPermitted(f"Failed to remove existing version directory {version_dir}: {str(e)}")
# Setup temp directory # Setup temp directory
import shutil
temp_dir = Settings.qt_installer_temp_path temp_dir = Settings.qt_installer_temp_path
temp_path = Path(temp_dir) temp_path = Path(temp_dir)
if temp_path.exists(): if not temp_path.exists():
shutil.rmtree(temp_dir) temp_path.mkdir(parents=True, exist_ok=True)
temp_path.mkdir(parents=True, exist_ok=True) else:
Settings.qt_installer_cleanup()
installer_path = temp_path / self._installer_filename installer_path = temp_path / self._installer_filename
self.logger.info(f"Downloading Qt installer to {installer_path}") self.logger.info(f"Downloading Qt installer to {installer_path}")
@@ -371,8 +373,16 @@ class CommercialInstaller:
install_cmd = self.package_manager.get_install_command(self.modules, temp_dir) install_cmd = self.package_manager.get_install_command(self.modules, temp_dir)
cmd = [*base_cmd, *install_cmd] cmd = [*base_cmd, *install_cmd]
self.logger.info(f"Running: {cmd}") log_cmd = cmd.copy()
safely_run(cmd, Settings.qt_installer_timeout) for i in range(len(log_cmd) - 1):
if log_cmd[i] == "--email" or log_cmd[i] == "--pw":
log_cmd[i + 1] = "***"
if not self.dry_run:
self.logger.info(f"Running: {log_cmd}")
safely_run(cmd, Settings.qt_installer_timeout)
else:
self.logger.info(f"Would run: {log_cmd}")
except Exception as e: except Exception as e:
self.logger.error(f"Installation failed: {str(e)}") self.logger.error(f"Installation failed: {str(e)}")

View File

@@ -22,6 +22,7 @@ import binascii
import hashlib import hashlib
import logging.config import logging.config
import os import os
import platform
import posixpath import posixpath
import secrets import secrets
import shutil import shutil
@@ -40,6 +41,7 @@ from xml.etree.ElementTree import Element
import humanize import humanize
import requests import requests
import requests.adapters import requests.adapters
from bs4 import BeautifulSoup
from defusedxml import ElementTree from defusedxml import ElementTree
from aqt.exceptions import ( from aqt.exceptions import (
@@ -62,6 +64,23 @@ def get_os_name() -> str:
raise ValueError(f"Unsupported operating system: {system}") raise ValueError(f"Unsupported operating system: {system}")
def get_os_arch() -> str:
"""
Returns a simplified os-arch string for the current system
"""
os_name = get_os_name()
machine = platform.machine().lower()
if machine in ["x86_64", "amd64"]:
arch = "x64"
elif machine in ["arm64", "aarch64"]:
arch = "arm64"
else:
arch = "x64" # Default to x64 for unknown architectures
return f"{os_name}-{arch}"
def get_qt_local_folder_path() -> Path: def get_qt_local_folder_path() -> Path:
os_name = get_os_name() os_name = get_os_name()
if os_name == "windows": if os_name == "windows":
@@ -76,13 +95,59 @@ def get_qt_account_path() -> Path:
return get_qt_local_folder_path() / "qtaccount.ini" return get_qt_local_folder_path() / "qtaccount.ini"
def get_qt_installers() -> dict[str, str]:
"""
Extracts Qt installer information from {Settings.baseurl}/official_releases/online_installers/
Maps OS types and architectures to their respective installer filenames
Returns:
dict: Mapping of OS identifiers to installer filenames with appropriate aliases
"""
url = f"{Settings.baseurl}/official_releases/online_installers/"
try:
response = requests.get(url, timeout=Settings.response_timeout)
response.raise_for_status()
soup = BeautifulSoup(response.text, "html.parser")
installers = {}
os_types = ["windows", "linux", "mac"]
for link in soup.find_all("a"):
filename = link.text.strip()
if "Parent Directory" in filename or not any(ext in filename.lower() for ext in [".exe", ".dmg", ".run"]):
continue
for os_type in os_types:
if os_type.lower() in filename.lower():
# Found an OS match, now look for architecture
if "arm64" in filename.lower():
installers[f"{os_type}-arm64"] = filename
elif "x64" in filename.lower():
installers[f"{os_type}-x64"] = filename
# Also add generic OS entry for x64 variants of Windows and Linux
if os_type in ["windows", "linux"]:
installers[os_type] = filename
else:
# Handle case with no explicit architecture
# Most likely for macOS which might just say "mac" without arch
installers[os_type] = filename
return installers
except requests.exceptions.RequestException as e:
print(f"Error fetching installer data: {e}")
return {}
except Exception as e:
print(f"Unexpected error processing installer data: {e}")
return {}
def get_qt_installer_name() -> str: def get_qt_installer_name() -> str:
installer_dict = { installer_dict = get_qt_installers()
"windows": "qt-unified-windows-x64-online.exe", return installer_dict[get_os_arch()]
"mac": "qt-unified-mac-x64-online.dmg",
"linux": "qt-unified-linux-x64-online.run",
}
return installer_dict[get_os_name()]
def get_qt_installer_path() -> Path: def get_qt_installer_path() -> Path:
@@ -427,8 +492,13 @@ class SettingsClass:
logging.info(f"Cache folder: {self.qt_installer_cache_path}") logging.info(f"Cache folder: {self.qt_installer_cache_path}")
logging.info(f"Temp folder: {self.qt_installer_temp_path}") logging.info(f"Temp folder: {self.qt_installer_temp_path}")
if Path(self.qt_installer_temp_path).exists():
shutil.rmtree(self.qt_installer_temp_path) temp_dir = self.qt_installer_temp_path
temp_path = Path(temp_dir)
if not temp_path.exists():
temp_path.mkdir(parents=True, exist_ok=True)
else:
self.qt_installer_cleanup()
def _get_config(self) -> ConfigParser: def _get_config(self) -> ConfigParser:
"""Safe getter for config that ensures it's initialized.""" """Safe getter for config that ensures it's initialized."""
@@ -616,10 +686,12 @@ class SettingsClass:
return self._get_config().getboolean("qtofficial", "unattended", fallback=True) return self._get_config().getboolean("qtofficial", "unattended", fallback=True)
def qt_installer_cleanup(self) -> None: def qt_installer_cleanup(self) -> None:
"""Control whether to use unattended installation flags.""" """Clean tmp folder."""
import shutil for item in Path(self.qt_installer_temp_path).iterdir():
if item.is_dir():
shutil.rmtree(self.qt_installer_temp_path) shutil.rmtree(item)
else:
item.unlink()
Settings = SettingsClass() Settings = SettingsClass()
@@ -647,7 +719,7 @@ def safely_run_save_output(cmd: List[str], timeout: int) -> Any:
raise raise
def extract_auth(args: List[str]) -> Tuple[str | None, str | None, List[str] | None]: def extract_auth(args: List[str]) -> Tuple[Union[str, None], Union[str, None], Union[List[str], None]]:
username = None username = None
password = None password = None
i = 0 i = 0

View File

@@ -368,6 +368,62 @@ class Cli:
else: else:
qt_version = args.qt_version qt_version = args.qt_version
Cli._validate_version_str(qt_version) Cli._validate_version_str(qt_version)
if hasattr(args, "use_official_installer") and args.use_official_installer is not None:
if len(args.use_official_installer) not in [0, 2]:
raise CliInputError(
"When providing arguments to --use-official-installer, exactly 2 arguments are required: "
"--use-official-installer email password"
)
self.logger.info("Using official Qt installer")
commercial_args = InstallArgParser()
# Core parameters required by install-qt-official
commercial_args.target = args.target
commercial_args.arch = self._set_arch(
args.arch, args.host, args.target, getattr(args, "qt_version", getattr(args, "qt_version_spec", ""))
)
commercial_args.version = qt_version
email = None
password = None
if len(args.use_official_installer) == 2:
email, password = args.use_official_installer
self.logger.info("Using credentials provided with --use-official-installer")
# Optional parameters
commercial_args.email = email or getattr(args, "email", None)
commercial_args.pw = password or getattr(args, "pw", None)
commercial_args.outputdir = args.outputdir
commercial_args.modules = args.modules
commercial_args.base = getattr(args, "base", None)
commercial_args.dry_run = getattr(args, "dry_run", False)
commercial_args.override = None
ignored_options = []
if getattr(args, "noarchives", False):
ignored_options.append("--noarchives")
if getattr(args, "autodesktop", False):
ignored_options.append("--autodesktop")
if getattr(args, "archives", None):
ignored_options.append("--archives")
if getattr(args, "timeout", False):
ignored_options.append("--timeout")
if getattr(args, "keep", False):
ignored_options.append("--keep")
if getattr(args, "archive_dest", False):
ignored_options.append("--archive_dest")
if ignored_options:
self.logger.warning("Options ignored because you requested the official installer:")
self.logger.warning(", ".join(ignored_options))
return self.run_install_qt_commercial(commercial_args, print_version=False)
archives = args.archives archives = args.archives
if args.noarchives: if args.noarchives:
if modules is None: if modules is None:
@@ -683,9 +739,10 @@ class Cli:
) )
show_list(meta) show_list(meta)
def run_install_qt_commercial(self, args: InstallArgParser) -> None: def run_install_qt_commercial(self, args: InstallArgParser, print_version: Optional[bool] = True) -> None:
"""Execute commercial Qt installation""" """Execute commercial Qt installation"""
self.show_aqt_version() if print_version:
self.show_aqt_version()
try: try:
if args.override: if args.override:
@@ -700,6 +757,7 @@ class Cli:
no_unattended=not Settings.qt_installer_unattended, no_unattended=not Settings.qt_installer_unattended,
username=username or args.email, username=username or args.email,
password=password or args.pw, password=password or args.pw,
dry_run=args.dry_run,
) )
else: else:
if not all([args.target, args.arch, args.version]): if not all([args.target, args.arch, args.version]):
@@ -716,12 +774,13 @@ class Cli:
base_url=args.base if args.base is not None else Settings.baseurl, base_url=args.base if args.base is not None else Settings.baseurl,
no_unattended=not Settings.qt_installer_unattended, no_unattended=not Settings.qt_installer_unattended,
modules=args.modules, modules=args.modules,
dry_run=args.dry_run,
) )
commercial_installer.install() commercial_installer.install()
Settings.qt_installer_cleanup() Settings.qt_installer_cleanup()
except Exception as e: except Exception as e:
self.logger.error(f"Error installing official installer {str(e)}") self.logger.error(f"Error installing official installer: {str(e)}")
finally: finally:
self.logger.info("Done") self.logger.info("Done")
@@ -793,6 +852,16 @@ class Cli:
"required. When enabled, this option installs the required desktop version automatically. " "required. When enabled, this option installs the required desktop version automatically. "
"It has no effect when the desktop installation is not required.", "It has no effect when the desktop installation is not required.",
) )
install_qt_parser.add_argument(
"--use-official-installer",
nargs="*",
default=None,
metavar=("EMAIL", "PASSWORD"),
help="Use the official Qt installer for installation instead of the aqt downloader. "
"Can be used without arguments or with email and password: --use-official-installer email password. "
"This redirects to install-qt-official. "
"Arguments not compatible with the official installer will be ignored.",
)
def _set_install_tool_parser(self, install_tool_parser): def _set_install_tool_parser(self, install_tool_parser):
install_tool_parser.set_defaults(func=self.run_install_tool) install_tool_parser.set_defaults(func=self.run_install_tool)
@@ -889,14 +958,12 @@ class Cli:
self.show_aqt_version() self.show_aqt_version()
# Create temporary directory for installer # Create temporary directory for installer
import shutil
from pathlib import Path
temp_dir = Settings.qt_installer_temp_path temp_dir = Settings.qt_installer_temp_path
temp_path = Path(temp_dir) temp_path = Path(temp_dir)
if temp_path.exists(): if not temp_path.exists():
shutil.rmtree(temp_dir) temp_path.mkdir(parents=True, exist_ok=True)
temp_path.mkdir(parents=True, exist_ok=True) else:
Settings.qt_installer_cleanup()
# Get installer based on OS # Get installer based on OS
installer_filename = get_qt_installer_name() installer_filename = get_qt_installer_name()

View File

@@ -16,6 +16,7 @@ All contributors, listed alphabetically, are:
* Adrian Eddy (fix the case of Android 6.7.0) * Adrian Eddy (fix the case of Android 6.7.0)
* Alberto Mardegan(ignore_hash option) * Alberto Mardegan(ignore_hash option)
* Alexandre @Kidev Poumaroux (official installer option, WASM fixes for Qt6.7.x)
* Andrew Wason (support arm64) * Andrew Wason (support arm64)
* Andrei Yankovich (tools ifw installation) * Andrei Yankovich (tools ifw installation)
* Aurélien Gâteau (patching to qmake) * Aurélien Gâteau (patching to qmake)
@@ -51,4 +52,4 @@ All contributors, listed alphabetically, are:
* @ypnos (Documents) * @ypnos (Documents)
and many other participants and contributors. and many other participants and contributors.
If you find a missing name to record, please feel free to tell me. If you find a missing name to record, please feel free to tell me.

View File

@@ -44,50 +44,6 @@ Options
* If none are inputed, the package ``qtX.Y.Z-essentials`` is downloaded (default Qt install, includes ``qtcreator``...) * If none are inputed, the package ``qtX.Y.Z-essentials`` is downloaded (default Qt install, includes ``qtcreator``...)
* If ``all`` is inputed, the package ``qtX.Y.Z-full`` is downloaded (includes everything) * If ``all`` is inputed, the package ``qtX.Y.Z-full`` is downloaded (includes everything)
.. dropdown:: Click to see all the special packages (for Linux Qt 6.8.1)
.. code-block:: text
Name: qt6.8.1-essentials
Display name: Qt 6.8.1 Linux x86_64 Essential Components
Description: Qt 6.8.1 Linux x86_64 Essential Libraries, Headers, and Tools
Version: 6.8.1
Components: qt.qt6.681.linux_gcc_64
Name: qt6.8.1-essentials-dev
Display name: Qt 6.8.1 Linux x86_64 Essential Components (dev)
Description: Qt 6.8.1 Linux x86_64 Essential Libraries, Headers, and Tools (dev)
Version: 6.8.1
Required aliases: qt6.8.1-essentials
Name: qt6.8.1-full
Display name: Qt 6.8.1 Linux x86_64 All Components with Sources
Description: Qt 6.8.1 Linux x86_64 All Libraries, Headers, Tools, and Sources
Version: 6.8.1
Components: qt.qt6.681.src,extensions.qtwebengine.681.src
Required aliases: qt6.8.1-essentials,qt6.8.1-addons,qt6.8.1-extensions
Optional components: extensions.qtinsighttracker.681.src
Name: qt6.8.1-full-dbg
Display name: Qt 6.8.1 Linux x86_64 All Components with Sources and Debug Information Files
Description: Qt 6.8.1 Linux x86_64 All Libraries, Headers, Tools, Sources, and Debug Information Files (dev)
Version: 6.8.1
Components: qt.qt6.681.debug_info.linux_gcc_64,qt.qt6.681.debug_info,extensions.qtwebengine.681.debug_information
Required aliases: qt6.8.1-full-dev
Name: qt6.8.1-full-dev
Display name: Qt 6.8.1 Linux x86_64 All Components with Sources (dev)
Description: Qt 6.8.1 Linux x86_64 All Libraries, Headers, Tools, and Sources (dev)
Version: 6.8.1
Required aliases: qt6.8.1-full
Name: qt6.8.1-sdk
Display name: Qt 6.8.1 Linux x86_64 SDK
Description: Qt 6.8.1 Linux x86_64 SDK Tools (Qt Creator, Ninja, and CMake)
Version: 6.8.1
Components: qt.tools.qtcreator_gui,qt.tools.cmake,qt.tools.ninja
Required aliases: qt6.8.1-full-dev,qt6.8.1-full-dbg
- ``--outputdir <path>`` - Installation directory (default: current directory) - ``--outputdir <path>`` - Installation directory (default: current directory)
- ``--override <args...>`` - Pass all remaining arguments directly to the Qt installer CLI - ``--override <args...>`` - Pass all remaining arguments directly to the Qt installer CLI
@@ -106,7 +62,7 @@ Options
- ``search_terms`` - Terms to search for in package names (grabs all that is not other options) - ``search_terms`` - Terms to search for in package names (grabs all that is not other options)
Override Mode Override Mode
------------ ----------------------
``install-qt-official`` supports an override mode that passes all arguments after ``--override`` directly to the Qt installer CLI, and will ignore all the other params except ``--email`` and ``--pw`` if given prior to it ``install-qt-official`` supports an override mode that passes all arguments after ``--override`` directly to the Qt installer CLI, and will ignore all the other params except ``--email`` and ``--pw`` if given prior to it
.. code-block:: bash .. code-block:: bash
@@ -122,7 +78,7 @@ When using override mode:
* `More info here <https://doc.qt.io/qt-6/get-and-install-qt-cli.html>`_ * `More info here <https://doc.qt.io/qt-6/get-and-install-qt-cli.html>`_
Examples Examples
-------- --------------
.. code-block:: bash .. code-block:: bash
# Standard installation # Standard installation
@@ -138,7 +94,7 @@ Examples
aqt install-qt-official --override install qt.qt6.680.gcc_64 --email user@example.com --pw pass aqt install-qt-official --override install qt.qt6.680.gcc_64 --email user@example.com --pw pass
Advanced configs Advanced configs
-------------- --------------------------
The file located in ``./aqt/settings.ini`` can be edited in the ``[qtofficial]`` part to fine tune the official installer (`more details here <https://doc.qt.io/qt-6/get-and-install-qt-cli.html#message-identifiers-for-auto-answer>`_): The file located in ``./aqt/settings.ini`` can be edited in the ``[qtofficial]`` part to fine tune the official installer (`more details here <https://doc.qt.io/qt-6/get-and-install-qt-cli.html#message-identifiers-for-auto-answer>`_):
.. code-block:: ini .. code-block:: ini

View File

@@ -13,33 +13,10 @@ from aqt.metadata import MetadataFactory, SimpleSpec, Version
def expected_help(actual, prefix=None): def expected_help(actual, prefix=None):
expected = ( expected = "usage: aqt [-h] [-c CONFIG]"
"usage: aqt [-h] [-c CONFIG]\n"
" {install-qt,install-tool,install-qt-official,list-qt-official,install-doc,install-example,"
"install-src,list-qt,list-tool,list-doc,list-example,list-src,help,version}\n"
" ...\n"
"\n"
"Another unofficial Qt Installer.\n"
"aqt helps you install Qt SDK, tools, examples and others\n"
"\n"
"option",
" -h, --help show this help message and exit\n"
" -c CONFIG, --config CONFIG\n"
" Configuration ini file.\n"
"\n"
"subcommands:\n"
" aqt accepts several subcommands:\n"
" install-* subcommands are commands that install components\n"
" list-* subcommands are commands that show available components\n"
"\n"
" {install-qt,install-tool,install-qt-official,list-qt-official,install-doc,install-example,install-src,"
"list-qt,list-tool,list-doc,list-example,list-src,help,version}\n"
" Please refer to each help message by using '--help' "
"with each subcommand\n",
)
if prefix is not None: if prefix is not None:
return actual.startswith(prefix + expected[0]) and actual.endswith(expected[1]) return actual.startswith(prefix + expected)
return actual.startswith(expected[0]) and actual.endswith(expected[1]) return actual.startswith(expected)
def test_cli_help(capsys): def test_cli_help(capsys):

View File

@@ -3,17 +3,15 @@ import shutil
import sys import sys
from pathlib import Path from pathlib import Path
from typing import Dict, List, Optional from typing import Dict, List, Optional
from urllib.parse import urlparse
import pytest import pytest
import requests import requests
from aqt.commercial import CommercialInstaller, QtPackageInfo, QtPackageManager from aqt.commercial import CommercialInstaller, QtPackageInfo, QtPackageManager
from aqt.exceptions import DiskAccessNotPermitted from aqt.exceptions import DiskAccessNotPermitted
from aqt.helper import Settings, download_installer, get_qt_account_path from aqt.helper import Settings, download_installer, get_os_name, get_qt_account_path, get_qt_installer_name
from aqt.installer import Cli from aqt.installer import Cli
from aqt.metadata import Version from aqt.metadata import Version
from tests.test_helper import mocked_requests_get
class CompletedProcess: class CompletedProcess:
@@ -51,15 +49,6 @@ class MockResponse:
yield self.content yield self.content
@pytest.fixture
def mock_settings(monkeypatch):
"""Setup test settings"""
# Instead of trying to set properties directly, we should mock the property getter
monkeypatch.setattr(Settings, "qt_installer_timeout", property(lambda self: 60))
monkeypatch.setattr(Settings, "qt_installer_cache_path", property(lambda self: str(Path.home() / ".qt" / "cache")))
monkeypatch.setattr(Settings, "qt_installer_temp_path", property(lambda self: str(Path.home() / ".qt" / "temp")))
@pytest.fixture @pytest.fixture
def commercial_installer(): def commercial_installer():
return CommercialInstaller( return CommercialInstaller(
@@ -206,23 +195,34 @@ def test_build_command(
assert cmd == expected_cmd assert cmd == expected_cmd
def test_commercial_installer_download_sha256(tmp_path, monkeypatch, commercial_installer): @pytest.mark.enable_socket
"""Test downloading of commercial installer""" @pytest.mark.parametrize(
"os, osarch, expected_suffix",
[
pytest.param("windows", "windows", ".exe"),
pytest.param("windows", "windows-x64", ".exe"),
pytest.param("windows", "windows-arm64", ".exe"),
pytest.param("linux", "linux", ".run"),
pytest.param("linux", "linux-x64", ".run"),
pytest.param("linux", "linux-arm64", ".run"),
pytest.param("mac", "mac-x64", ".dmg"),
],
)
def test_commercial_installer_names(monkeypatch, os, osarch, expected_suffix):
"""Test installer names finder"""
def mock_getUrl(url, *args, **kwargs): monkeypatch.setattr("aqt.helper.get_os_arch", lambda: osarch)
hash_filename = str(urlparse(url).path.split("/")[-1])
assert hash_filename.endswith(".sha256")
filename = hash_filename[: -len(".sha256")]
return f"07b3ef4606b712923a14816b1cfe9649687e617d030fc50f948920d784c0b1cd {filename}"
monkeypatch.setattr("aqt.helper.getUrl", mock_getUrl) installer_name = get_qt_installer_name()
monkeypatch.setattr(requests.Session, "get", mocked_requests_get)
target_path = tmp_path / "qt-installer" assert installer_name.endswith(expected_suffix)
timeout = (Settings.connection_timeout, Settings.response_timeout) if os == get_os_name() and osarch in ["windows", "linux", "mac-x64"]:
download_installer(commercial_installer.base_url, commercial_installer._installer_filename, target_path, timeout) target_path = Settings.qt_installer_temp_path / Path(installer_name)
assert target_path.exists() base_url = Settings.baseurl
timeout = (Settings.connection_timeout, Settings.response_timeout)
download_installer(base_url, installer_name, target_path, timeout)
assert True
@pytest.mark.parametrize( @pytest.mark.parametrize(
@@ -275,6 +275,17 @@ def test_get_install_command(monkeypatch, modules: Optional[List[str]], expected
"stopProcessesForUpdates=Cancel,installationErrorWithCancel=Cancel,installationErrorWithIgnore=Ignore," "stopProcessesForUpdates=Cancel,installationErrorWithCancel=Cancel,installationErrorWithIgnore=Ignore,"
"AssociateCommonFiletypes=Yes,telemetry-question=No install qt.{}.{}.{}", "AssociateCommonFiletypes=Yes,telemetry-question=No install qt.{}.{}.{}",
), ),
(
"install-qt linux desktop 6.8.1 {} --outputdir ./install-qt-flag --use-official-installer {} {}",
{"windows": "win64_msvc2022_64", "linux": "linux_gcc_64", "mac": "clang_64"},
["./install-qt-official", "qt6", "681"],
"{} --email ******** --pw ******** --root {} "
"--accept-licenses --accept-obligations "
"--confirm-command "
"--auto-answer OperationDoesNotExistError=Ignore,OverwriteTargetDirectory=Yes,"
"stopProcessesForUpdates=Cancel,installationErrorWithCancel=Cancel,installationErrorWithIgnore=Ignore,"
"AssociateCommonFiletypes=Yes,telemetry-question=No install qt.{}.{}.{}",
),
], ],
) )
def test_install_qt_commercial( def test_install_qt_commercial(
@@ -301,7 +312,10 @@ def test_install_qt_commercial(
password = TEST_PASSWORD password = TEST_PASSWORD
formatted_cmd = cmd.format(arch, email, password) formatted_cmd = cmd.format(arch, email, password)
formatted_expected = expected_command.format(current_platform, abs_out, *details[1:], arch) if expected_command.startswith("install-qt-official"):
formatted_expected = expected_command.format(current_platform, abs_out, *details[1:], arch)
else:
formatted_expected = expected_command.format(get_qt_installer_name(), abs_out, *details[1:], arch)
cli = Cli() cli = Cli()
cli._setup_settings() cli._setup_settings()
@@ -330,7 +344,7 @@ def test_install_qt_commercial(
else: else:
cli.run(new_cmd.split()) cli.run(new_cmd.split())
def modify_qt_config(content): def modify_qt_config(content, endwith):
""" """
Takes content of INI file as string and returns modified content Takes content of INI file as string and returns modified content
""" """
@@ -339,12 +353,12 @@ def test_install_qt_commercial(
modified = [] modified = []
for line in lines: for line in lines:
# Check if we're entering qtofficial section
if line.strip() == "[qtofficial]": if line.strip() == "[qtofficial]":
in_qt_commercial = True in_qt_commercial = True
# If in qtofficial section, look for the target line if endwith is not None and endwith in ["Yes", "No"] and "overwrite_target_directory : " in line:
if in_qt_commercial and "overwrite_target_directory : No" in line: line = f"overwrite_target_directory : {endwith}"
elif in_qt_commercial and "overwrite_target_directory : No" in line:
line = "overwrite_target_directory : Yes" line = "overwrite_target_directory : Yes"
elif in_qt_commercial and "overwrite_target_directory : Yes" in line: elif in_qt_commercial and "overwrite_target_directory : Yes" in line:
line = "overwrite_target_directory : No" line = "overwrite_target_directory : No"
@@ -359,7 +373,10 @@ def test_install_qt_commercial(
with open(config_path, "r") as f: with open(config_path, "r") as f:
content = f.read() content = f.read()
modified_content = modify_qt_config(content) if expected_command.startswith("install-qt-official"):
modified_content = modify_qt_config(content, None)
else:
modified_content = modify_qt_config(content, "No")
with open(config_path, "w") as f: with open(config_path, "w") as f:
f.write(modified_content) f.write(modified_content)

View File

@@ -1694,7 +1694,8 @@ def test_install_qt6_wasm_autodesktop(monkeypatch, capsys, version, str_version,
r"(?:INFO : Found extension .*?\n)*" r"(?:INFO : Found extension .*?\n)*"
r"(?:INFO : Downloading (?:qt[^\n]*|icu[^\n]*)\n" r"(?:INFO : Downloading (?:qt[^\n]*|icu[^\n]*)\n"
r"Finished installation of .*?\.7z in \d+\.\d+\n)*" r"Finished installation of .*?\.7z in \d+\.\d+\n)*"
r"(?:INFO : Patching (?:/tmp/[^/]+|[A-Za-z]:[\\/].*?)/6\.8\.0/wasm_singlethread/bin/(?:qmake(?:6)?|qtpaths(?:6)?|target_qt\.conf)\n)*" r"(?:INFO : Patching (?:/tmp/[^/]+|[A-Za-z]:[\\/].*?)/6\.8\.0/wasm_singlethread/bin/"
r"(?:qmake(?:6)?|qtpaths(?:6)?|target_qt\.conf)\n)*"
r"INFO : \n" r"INFO : \n"
r"INFO : Autodesktop will now install linux desktop 6\.8\.0 linux_gcc_64 as required by Qt6-WASM\n" r"INFO : Autodesktop will now install linux desktop 6\.8\.0 linux_gcc_64 as required by Qt6-WASM\n"
r"INFO : aqtinstall\(aqt\) v.*? on Python 3.*?\n" r"INFO : aqtinstall\(aqt\) v.*? on Python 3.*?\n"