From 976998d67ec896734c2b933fc1f8867d6397c58d Mon Sep 17 00:00:00 2001 From: Alexandre 'Kidev' Poumaroux <1204936+Kidev@users.noreply.github.com> Date: Sat, 11 Jan 2025 23:02:47 +0100 Subject: [PATCH] Add --override super command --- aqt/commercial.py | 15 +++++--- aqt/installer.py | 93 +++++++++++++++++++++++++++-------------------- 2 files changed, 64 insertions(+), 44 deletions(-) diff --git a/aqt/commercial.py b/aqt/commercial.py index 280cec93..79e3b3ed 100644 --- a/aqt/commercial.py +++ b/aqt/commercial.py @@ -46,10 +46,12 @@ def __init__( installation_error_with_ignore: str = "Ignore", associate_common_filetypes: str = "Yes", telemetry: str = "No", + override: Optional[list[str]] = None, ): + self.override = override self.target = target self.arch = arch or "" - self.version = Version(version) if version else Version() + self.version = Version(version) if version else Version("0.0.0") self.username = username self.password = password self.output_dir = output_dir @@ -100,6 +102,8 @@ def _download_installer(self, target_path: Path) -> None: with open(target_path, "wb") as f: for chunk in response.iter_content(chunk_size=8192): f.write(chunk) + if self.os_name != "windows": + os.chmod(target_path, 0o500) except Exception as e: raise RuntimeError(f"Failed to download installer: {e}") @@ -107,14 +111,15 @@ def _get_package_name(self) -> str: qt_version = f"{self.version.major}{self.version.minor}{self.version.patch}" return f"qt.qt{self.version.major}.{qt_version}.{self.arch}" - def _exec_qt_installer(self, cmd: list[str], working_dir: str) -> None: - """Execute the Qt installer command with proper path handling and security""" - def _get_install_command(self, installer_path: Path) -> list[str]: """Build the installation command array""" # Start with installer path (will be replaced with absolute path in _exec_qt_installer) cmd = [str(installer_path)] + # When override is specified, only use the installer path and the override parameters + if self.override: + return cmd + self.override.split() + # Add authentication if provided if self.username and self.password: cmd.extend(["--email", self.username, "--pw", self.password]) @@ -190,7 +195,7 @@ def install(self) -> None: safe_cmd[email_index + 1] = "********" self.logger.info(f"Running: {' '.join(safe_cmd)}") - subprocess.run([self._installer_filename] + cmd, shell=False, check=True, cwd=temp_dir) + subprocess.run(cmd, shell=False, check=True, cwd=temp_dir) except subprocess.CalledProcessError as e: self.logger.error(f"Installation failed with exit code {e.returncode}") diff --git a/aqt/installer.py b/aqt/installer.py index af272eef..0416fad5 100644 --- a/aqt/installer.py +++ b/aqt/installer.py @@ -125,6 +125,7 @@ class CommonInstallArgParser(BaseArgumentParser): class InstallArgParser(CommonInstallArgParser): """Install-qt arguments and options""" + override: Optional[List[str]] arch: Optional[str] qt_version: str qt_version_spec: str @@ -672,40 +673,40 @@ def run_install_qt_commercial(self, args: InstallArgParser) -> None: """Execute commercial Qt installation""" self.show_aqt_version() - if args.base is not None: - base = args.base - else: - base = Settings.baseurl - if args.timeout is not None: - timeout = args.timeout + if args.override: + # When override is used, we only need minimal parameters + commercial_installer = CommercialInstaller( + target="", # Empty string as placeholder + arch="", + version=None, + logger=self.logger, + timeout=args.timeout if args.timeout is not None else Settings.response_timeout, + base_url=args.base if args.base is not None else Settings.baseurl, + override=args.override, + ) else: - timeout = Settings.response_timeout - - target = args.target - arch = args.arch - version = args.version - username = args.user - password = args.password - output_dir = args.outputdir - - commercial_installer = CommercialInstaller( - target=target, - arch=arch, - version=version, - username=username, - password=password, - output_dir=output_dir, - logger=self.logger, - timeout=timeout, - base_url=base, - operation_does_not_exist_error=args.operation_does_not_exist_error, - overwrite_target_dir=args.overwrite_target_dir, - stop_processes_for_updates=args.stop_processes_for_updates, - installation_error_with_cancel=args.installation_error_with_cancel, - installation_error_with_ignore=args.installation_error_with_ignore, - associate_common_filetypes=args.associate_common_filetypes, - telemetry=args.telemetry, - ) + # Original validation and installer creation + if not all([args.target, args.arch, args.version]): + raise CliInputError("target, arch, and version are required when not using --override") + + commercial_installer = CommercialInstaller( + target=args.target, + arch=args.arch, + version=args.version, + username=args.user, + password=args.password, + output_dir=args.outputdir, + logger=self.logger, + timeout=args.timeout if args.timeout is not None else Settings.response_timeout, + base_url=args.base if args.base is not None else Settings.baseurl, + operation_does_not_exist_error=args.operation_does_not_exist_error, + overwrite_target_dir=args.overwrite_target_dir, + stop_processes_for_updates=args.stop_processes_for_updates, + installation_error_with_cancel=args.installation_error_with_cancel, + installation_error_with_ignore=args.installation_error_with_ignore, + associate_common_filetypes=args.associate_common_filetypes, + telemetry=args.telemetry, + ) try: commercial_installer.install() @@ -808,19 +809,33 @@ def _set_install_tool_parser(self, install_tool_parser): def _set_install_qt_commercial_parser(self, install_qt_commercial_parser) -> None: install_qt_commercial_parser.set_defaults(func=self.run_install_qt_commercial) + + # Create mutually exclusive group for override vs standard parameters + exclusive_group = install_qt_commercial_parser.add_mutually_exclusive_group() + exclusive_group.add_argument( + "--override", + help="Will ignore all other parameters and use everything after this parameter as " + "input for the official Qt installer", + ) + + # Make standard arguments optional when override is used by adding a custom action + class ConditionalRequiredAction(argparse.Action): + def __call__(self, parser, namespace, values, option_string=None): + if not hasattr(namespace, "override") or not namespace.override: + setattr(namespace, self.dest, values) + install_qt_commercial_parser.add_argument( "target", + nargs="?", choices=["desktop", "android", "ios"], help="Target platform", + action=ConditionalRequiredAction, ) install_qt_commercial_parser.add_argument( - "arch", - help="Target architecture", - ) - install_qt_commercial_parser.add_argument( - "version", - help="Qt version", + "arch", nargs="?", help="Target architecture", action=ConditionalRequiredAction ) + install_qt_commercial_parser.add_argument("version", nargs="?", help="Qt version", action=ConditionalRequiredAction) + install_qt_commercial_parser.add_argument( "--user", help="Qt account username",