Merge pull request #909 from Kidev/fix_linux_version
Some checks failed
Test on GH actions environment / test (standard, windows-latest, 3.13, 6.7.3) (push) Has been cancelled
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
Publish Python 🐍 distributions 📦 to PyPI / publish Python 🐍 distributions 📦 to PyPI (push) Has been cancelled

Fix arch guessing error when version not fully qualified, fix list-qt android, add --UNSAFE-ignore-hash, add --use-official-installer to list-qt
This commit is contained in:
Hiroshi Miura
2025-05-05 00:36:25 +09:00
committed by GitHub
3 changed files with 144 additions and 10 deletions

View File

@@ -467,12 +467,14 @@ class SettingsClass:
"config": None,
"configfile": None,
"loggingconf": None,
"_ignore_hash_override": None,
"_lock": Lock(),
}
def __init__(self) -> None:
self.config: Optional[ConfigParser]
self._lock: Lock
self._ignore_hash_override: Optional[bool] = None
self._initialize()
def __new__(cls, *p, **k):
@@ -506,6 +508,10 @@ class SettingsClass:
assert self.config is not None
return self.config
def set_ignore_hash_for_session(self, value: bool) -> None:
"""Override the INSECURE_NOT_FOR_PRODUCTION_ignore_hash setting for the current session without modifying config."""
self._ignore_hash_override = value
def load_settings(self, file: Optional[Union[str, TextIO]] = None) -> None:
if self.config is None:
return
@@ -604,6 +610,8 @@ class SettingsClass:
@property
def ignore_hash(self):
if self._ignore_hash_override is not None:
return self._ignore_hash_override
return self.config.getboolean("requests", "INSECURE_NOT_FOR_PRODUCTION_ignore_hash", fallback=False)
@property

View File

@@ -105,6 +105,9 @@ class ListArgumentParser(BaseArgumentParser):
qt_version_spec: str
spec: str
target: str
email: Optional[str]
pw: Optional[str]
search_terms: Optional[str]
class ListToolArgumentParser(ListArgumentParser):
@@ -369,6 +372,9 @@ class Cli:
qt_version = args.qt_version
Cli._validate_version_str(qt_version)
if qt_version != qt_version_or_spec:
arch = self._set_arch(args.arch, os_name, target, 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]:
@@ -659,6 +665,62 @@ class Cli:
def run_list_qt(self, args: ListArgumentParser):
"""Print versions of Qt, extensions, modules, architectures"""
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 for search")
commercial_search_args = ListArgumentParser()
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")
commercial_search_args.email = email or getattr(args, "email", None)
commercial_search_args.pw = password or getattr(args, "pw", None)
target_str = ""
version_str = ""
if hasattr(args, "target") and args.target is not None:
target_str = args.target
if hasattr(args, "arch") and args.arch is not None:
try:
version = Version(args.arch)
version_str = f"{version.major}{version.minor}{version.patch}"
except Exception as e:
self.logger.warning(f"{e}. Ignoring 'arch' value")
commercial_search_args.search_terms = [rf"^.*{re.escape(version_str)}\.{re.escape(target_str)}.*$"]
ignored_options = []
if getattr(args, "extensions", False):
ignored_options.append("--extensions")
if getattr(args, "extension", False):
ignored_options.append("--extension")
if getattr(args, "modules", None):
ignored_options.append("--modules")
if getattr(args, "long_modules", False):
ignored_options.append("--long_modules")
if getattr(args, "spec", False):
ignored_options.append("--spec")
if getattr(args, "archives", False):
ignored_options.append("--archives")
if getattr(args, "latest-version", False):
ignored_options.append("--latest-version")
if ignored_options:
self.logger.warning("Options ignored because you requested the official installer:")
self.logger.warning(", ".join(ignored_options))
return self.run_list_qt_commercial(commercial_search_args, print_version=False)
if args.extensions:
self._warn_on_deprecated_parameter("extensions", args.extensions)
self.logger.warning(
@@ -818,10 +880,11 @@ class Cli:
install_qt_parser.add_argument(
"arch",
nargs="?",
help="\ntarget linux/desktop: gcc_64, wasm_32"
help="\ntarget linux/desktop: linux_gcc_64, gcc_64, wasm_32"
"\ntarget mac/desktop: clang_64, wasm_32"
"\ntarget mac/ios: ios"
"\nwindows/desktop: win64_msvc2019_64, win32_msvc2019"
"\nwindows/desktop: win64_msvc2022_64"
"\n win64_msvc2019_64, win32_msvc2019"
"\n win64_msvc2017_64, win32_msvc2017"
"\n win64_msvc2015_64, win32_msvc2015"
"\n win64_mingw81, win32_mingw81"
@@ -953,9 +1016,10 @@ class Cli:
help="Search terms (all non-option arguments are treated as search terms)",
)
def run_list_qt_commercial(self, args) -> None:
def run_list_qt_commercial(self, args: ListArgumentParser, print_version: Optional[bool] = True) -> None:
"""Execute Qt commercial package listing."""
self.show_aqt_version()
if print_version:
self.show_aqt_version()
# Create temporary directory for installer
temp_dir = Settings.qt_installer_temp_path
@@ -1126,6 +1190,16 @@ class Cli:
help="Filter output so that only versions that match the specification are printed. "
'IE: `aqt list-qt windows desktop --spec "5.12"` prints all versions beginning with 5.12',
)
list_parser.add_argument(
"--use-official-installer",
nargs="*",
default=None,
metavar=("EMAIL", "PASSWORD"),
help="Use the official Qt installer for research instead of the aqt researcher. "
"Can be used without arguments or with email and password: --use-official-installer email password. "
"This redirects to list-qt-official. "
"Arguments not compatible with the official installer will be ignored.",
)
output_modifier_exclusive_group = list_parser.add_mutually_exclusive_group()
output_modifier_exclusive_group.add_argument(
"--modules",
@@ -1265,6 +1339,11 @@ class Cli:
action="store_true",
help="Print what would be downloaded and installed without actually doing it",
)
subparser.add_argument(
"--UNSAFE-ignore-hash",
action="store_true",
help="UNSAFE: Skip hash verification of downloaded files. Use at your own risk.",
)
def _set_module_options(self, subparser):
subparser.add_argument("-m", "--modules", nargs="*", help="Specify extra modules to install")
@@ -1321,6 +1400,22 @@ class Cli:
else:
Settings.load_settings()
# Set ignore_hash to True if --UNSAFE-ignore-hash flag was passed
if args is not None and hasattr(args, "UNSAFE_ignore_hash") and args.UNSAFE_ignore_hash:
self.logger.warning(
"************************************************************************************************"
)
self.logger.warning(
"Hash verification is disabled. This is UNSAFE and may allow malicious files to be downloaded."
)
self.logger.warning(
"If your install mirror hosts malicious files, you won't be able to know. Use at your own risk."
)
self.logger.warning(
"************************************************************************************************"
)
Settings.set_ignore_hash_for_session(True)
@staticmethod
def _validate_version_str(
version_str: Optional[str],

View File

@@ -250,7 +250,7 @@ class ArchiveId:
"mac": ["android", "desktop", "ios"],
"linux": ["android", "desktop"],
"linux_arm64": ["desktop"],
"all_os": ["wasm", "qt"],
"all_os": ["wasm", "qt", "android"],
}
EXTENSIONS_REQUIRED_ANDROID_QT6 = {"x86_64", "x86", "armv7", "arm64_v8a"}
ALL_EXTENSIONS = {
@@ -779,14 +779,22 @@ class MetadataFactory:
return arches
def fetch_versions(self, extension: str = "") -> Versions:
def match_any_ext(ver: Version) -> bool:
return (
self.archive_id.host == "all_os"
and self.archive_id.target in ArchiveId.TARGETS_FOR_HOST["all_os"]
and ver in SimpleSpec("6.7.*")
)
def filter_by(ver: Version, ext: str) -> bool:
return (self.spec is None or ver in self.spec) and ext == extension
return (self.spec is None or ver in self.spec) and (ext == extension or match_any_ext(ver))
versions_extensions = self.get_versions_extensions(
self.fetch_http(self.archive_id.to_url(), False), self.archive_id.category
)
versions = sorted([ver for ver, ext in versions_extensions if ver is not None and filter_by(ver, ext)])
versions = sorted({ver for ver, ext in versions_extensions if ver is not None and filter_by(ver, ext)})
grouped = cast(Iterable[Tuple[int, Iterable[Version]]], itertools.groupby(versions, lambda version: version.minor))
return Versions(grouped)
def fetch_latest_version(self, ext: str) -> Optional[Version]:
@@ -912,9 +920,32 @@ class MetadataFactory:
def get_versions_extensions(self, html_doc: str, category: str) -> Iterator[Tuple[Optional[Version], str]]:
def folder_to_version_extension(folder: str) -> Tuple[Optional[Version], str]:
components = folder.split("_", maxsplit=2)
ext = "" if len(components) < 3 else components[2]
ver = "" if len(components) < 2 else components[1]
ext = ""
ver = ""
# Special case for Qt6.7 unique format
if folder.startswith("qt6_7_"):
# Split the input into version and extension parts
# For qt6_7_3_arm64_v8a, we want to extract "6_7_3" and "arm64_v8a"
# Should not be more than qt6_7_3_backup (extension should not have _, but you know...)
# Remove the "qt" prefix first
remainder = folder[2:]
# Split the first 3 parts for the version (6_7_3)
version_parts = remainder.split("_", 3)[:3]
ver = "_".join(version_parts)
# Everything after version is the extension
if len(remainder.split("_", 3)) > 3:
ext = remainder.split("_", 3)[3]
else:
ext = ""
else:
components = folder.split("_", maxsplit=2)
ext = "" if len(components) < 3 else components[2]
ver = "" if len(components) < 2 else components[1]
return (
get_semantic_version(qt_ver=ver, is_preview="preview" in ext),
ext,