mirror of
https://github.com/Omni-guides/Jackify.git
synced 2026-06-17 13:27:44 +02:00
Sync from development - prepare for v0.4.0
This commit is contained in:
@@ -91,12 +91,17 @@ class ConfigureModlistCommand:
|
||||
try:
|
||||
# Build configuration context from args
|
||||
context = self._build_context_from_args(args)
|
||||
|
||||
|
||||
# Use legacy implementation for now - will migrate to backend services later
|
||||
result = self._execute_legacy_configuration(context)
|
||||
|
||||
|
||||
logger.info("Finished non-interactive modlist configuration")
|
||||
return 0 if result is not True else 1
|
||||
|
||||
if not getattr(args, 'skip_confirmation', False) and context.get('install_dir'):
|
||||
from jackify.backend.handlers.modlist_install_cli_ttw import prompt_ttw_if_eligible
|
||||
prompt_ttw_if_eligible(context['install_dir'], context.get('modlist_name') or '')
|
||||
|
||||
return 0 if result is True else 1
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to configure modlist: {e}")
|
||||
@@ -156,4 +161,4 @@ class ConfigureModlistCommand:
|
||||
# The _configure_new_modlist method already calls run_modlist_configuration_phase internally
|
||||
# So we don't need to call it again here
|
||||
|
||||
return result
|
||||
return result
|
||||
|
||||
89
jackify/frontends/cli/commands/setup_mo2.py
Normal file
89
jackify/frontends/cli/commands/setup_mo2.py
Normal file
@@ -0,0 +1,89 @@
|
||||
"""
|
||||
Setup Mod Organizer 2 Command
|
||||
|
||||
CLI interface for downloading and configuring a standalone MO2 instance.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
from jackify.backend.services.mo2_setup_service import MO2SetupService, _is_dangerous_path
|
||||
from jackify.shared.colors import COLOR_PROMPT, COLOR_RESET, COLOR_INFO, COLOR_SUCCESS, COLOR_ERROR
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SetupMO2Command:
|
||||
"""CLI command for standalone MO2 setup"""
|
||||
|
||||
def run(self):
|
||||
"""Execute the MO2 setup workflow"""
|
||||
print(f"\n{COLOR_INFO}=== Setup Mod Organizer 2 ==={COLOR_RESET}\n")
|
||||
print("Downloads the latest MO2 release, adds it to Steam, and configures a Proton prefix.")
|
||||
print("Steam will be restarted during this process.\n")
|
||||
|
||||
# Install directory
|
||||
default_dir = str(Path.home() / "ModOrganizer2")
|
||||
dir_input = input(
|
||||
f"{COLOR_PROMPT}Installation directory [{default_dir}]: {COLOR_RESET}"
|
||||
).strip()
|
||||
install_dir = Path(dir_input) if dir_input else Path(default_dir)
|
||||
|
||||
# Danger check
|
||||
if _is_dangerous_path(install_dir):
|
||||
print(f"{COLOR_ERROR}Refusing to install to a dangerous directory: {install_dir}{COLOR_RESET}")
|
||||
input(f"\n{COLOR_PROMPT}Press Enter to continue...{COLOR_RESET}")
|
||||
return
|
||||
|
||||
# Non-empty directory warning
|
||||
if install_dir.exists() and any(install_dir.iterdir()):
|
||||
print(f"\n{COLOR_ERROR}[WARN] Directory is not empty: {install_dir}{COLOR_RESET}")
|
||||
confirm = input(
|
||||
f"{COLOR_PROMPT}Files may be overwritten. Continue anyway? (y/N): {COLOR_RESET}"
|
||||
).strip().lower()
|
||||
if confirm != 'y':
|
||||
print("Cancelled.")
|
||||
input(f"\n{COLOR_PROMPT}Press Enter to continue...{COLOR_RESET}")
|
||||
return
|
||||
|
||||
# Shortcut name
|
||||
default_name = "Mod Organizer 2"
|
||||
name_input = input(
|
||||
f"{COLOR_PROMPT}Steam shortcut name [{default_name}]: {COLOR_RESET}"
|
||||
).strip()
|
||||
shortcut_name = name_input if name_input else default_name
|
||||
|
||||
# Confirm
|
||||
print(f"\n{COLOR_INFO}Install directory: {install_dir}{COLOR_RESET}")
|
||||
print(f"{COLOR_INFO}Shortcut name: {shortcut_name}{COLOR_RESET}")
|
||||
confirm = input(
|
||||
f"\n{COLOR_PROMPT}Proceed? (Y/n): {COLOR_RESET}"
|
||||
).strip().lower()
|
||||
if confirm == 'n':
|
||||
print("Cancelled.")
|
||||
input(f"\n{COLOR_PROMPT}Press Enter to continue...{COLOR_RESET}")
|
||||
return
|
||||
|
||||
print(f"\n{COLOR_INFO}Starting MO2 setup...{COLOR_RESET}\n")
|
||||
|
||||
def _progress(msg: str):
|
||||
print(f"{COLOR_INFO} {msg}{COLOR_RESET}")
|
||||
|
||||
service = MO2SetupService()
|
||||
success, app_id, error_msg = service.setup_mo2(
|
||||
install_dir=install_dir,
|
||||
shortcut_name=shortcut_name,
|
||||
progress_callback=_progress,
|
||||
)
|
||||
|
||||
if success:
|
||||
print(f"\n{COLOR_SUCCESS}{'='*60}{COLOR_RESET}")
|
||||
print(f"{COLOR_SUCCESS}MO2 setup complete!{COLOR_RESET}")
|
||||
print(f"{COLOR_SUCCESS}{'='*60}{COLOR_RESET}")
|
||||
print(f"{COLOR_INFO}Steam AppID: {app_id}{COLOR_RESET}")
|
||||
print(f"{COLOR_INFO}Launch Mod Organizer 2 from your Steam library.{COLOR_RESET}")
|
||||
else:
|
||||
print(f"\n{COLOR_ERROR}MO2 setup failed: {error_msg}{COLOR_RESET}")
|
||||
print(f"{COLOR_INFO}Check logs for details.{COLOR_RESET}")
|
||||
|
||||
input(f"\n{COLOR_PROMPT}Press Enter to continue...{COLOR_RESET}")
|
||||
@@ -33,11 +33,9 @@ from jackify.backend.handlers.filesystem_handler import FileSystemHandler
|
||||
from jackify.backend.handlers.path_handler import PathHandler
|
||||
from jackify.backend.handlers.shortcut_handler import ShortcutHandler
|
||||
from jackify.backend.handlers.menu_handler import MenuHandler
|
||||
from jackify.backend.handlers.mo2_handler import MO2Handler
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class JackifyCLI:
|
||||
"""Main application class for Jackify CLI Frontend"""
|
||||
|
||||
@@ -92,10 +90,6 @@ class JackifyCLI:
|
||||
self.selected_modlist = None
|
||||
self.setup_complete = False
|
||||
|
||||
def _debug_print(self, message):
|
||||
"""Print debug message only if debug mode is enabled"""
|
||||
if hasattr(self, '_debug_mode') and self._debug_mode:
|
||||
logger.debug(message)
|
||||
|
||||
def _configure_logging_early(self):
|
||||
"""Configure logging to be quiet during initialization, will be adjusted after arg parsing"""
|
||||
@@ -113,22 +107,40 @@ class JackifyCLI:
|
||||
"""Configure final logging level based on parsed arguments"""
|
||||
# Use the existing LoggingHandler for proper log rotation
|
||||
from jackify.backend.handlers.logging_handler import LoggingHandler
|
||||
from jackify.shared.paths import get_jackify_logs_dir
|
||||
|
||||
# Set up CLI-specific logging with rotation
|
||||
logging_handler = LoggingHandler()
|
||||
logging_handler.rotate_log_for_logger('jackify-cli', 'Modlist_Install_workflow_cli.log')
|
||||
cli_logger = logging_handler.setup_logger('jackify-cli', 'Modlist_Install_workflow_cli.log')
|
||||
# Keep CLI logging in the canonical modlist workflow log file.
|
||||
logging_handler.rotate_log_for_logger('jackify-cli', 'Modlist_Install_workflow.log')
|
||||
cli_logger = logging_handler.setup_logger('jackify-cli', 'Modlist_Install_workflow.log')
|
||||
|
||||
# Remove legacy CLI log artifact if present (old naming path no longer used).
|
||||
try:
|
||||
legacy_cli_log = get_jackify_logs_dir() / "Modlist_Install_workflow_cli.log"
|
||||
if legacy_cli_log.exists():
|
||||
legacy_cli_log.unlink()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Configure logging level
|
||||
if self.args.debug:
|
||||
cli_logger.setLevel(logging.DEBUG)
|
||||
root_level = logging.DEBUG
|
||||
print("Debug logging enabled for console and file")
|
||||
elif self.args.verbose:
|
||||
cli_logger.setLevel(logging.INFO)
|
||||
root_level = logging.INFO
|
||||
print("Verbose logging enabled for console and file")
|
||||
else:
|
||||
# Keep it at WARNING level for clean startup
|
||||
# Keep console clean in normal mode; details remain in workflow log.
|
||||
cli_logger.setLevel(logging.WARNING)
|
||||
root_level = logging.ERROR
|
||||
|
||||
root_logger = logging.getLogger()
|
||||
root_logger.setLevel(root_level)
|
||||
for handler in root_logger.handlers:
|
||||
handler.setLevel(root_level)
|
||||
|
||||
def _is_steamdeck(self):
|
||||
"""Check if running on Steam Deck"""
|
||||
@@ -192,7 +204,7 @@ class JackifyCLI:
|
||||
def _check_for_updates_on_startup(self):
|
||||
"""Check for updates on startup in background thread"""
|
||||
try:
|
||||
self._debug_print("Checking for updates on startup...")
|
||||
logger.debug("Checking for updates on startup...")
|
||||
|
||||
def update_check_callback(update_info):
|
||||
"""Handle update check results"""
|
||||
@@ -207,15 +219,15 @@ class JackifyCLI:
|
||||
print(f"\nTo update, run: jackify --update")
|
||||
print("Or visit: https://github.com/Omni-guides/Jackify/releases")
|
||||
else:
|
||||
self._debug_print("No updates available")
|
||||
logger.debug("No updates available")
|
||||
except Exception as e:
|
||||
self._debug_print(f"Error showing update info: {e}")
|
||||
logger.debug(f"Error showing update info: {e}")
|
||||
|
||||
# Check for updates in background
|
||||
self.backend_services['update_service'].check_for_updates_async(update_check_callback)
|
||||
|
||||
except Exception as e:
|
||||
self._debug_print(f"Error checking for updates on startup: {e}")
|
||||
logger.debug(f"Error checking for updates on startup: {e}")
|
||||
# Continue anyway - don't block startup on update check errors
|
||||
|
||||
def _handle_update(self):
|
||||
@@ -326,7 +338,6 @@ class JackifyCLI:
|
||||
self.menu_handler = self.menu # Alias for backend compatibility
|
||||
|
||||
# Add MO2 handler to the menu handler for additional tasks menu
|
||||
self.menu.mo2_handler = MO2Handler(self.menu)
|
||||
|
||||
# Set steamdeck attribute that menus expect
|
||||
self.steamdeck = self.system_info.is_steamdeck
|
||||
@@ -359,24 +370,24 @@ class JackifyCLI:
|
||||
# Now that we have args, configure logging properly
|
||||
self._configure_logging_final()
|
||||
|
||||
self._debug_print('Initializing Jackify CLI Frontend')
|
||||
self._debug_print('JackifyCLI.run() called')
|
||||
self._debug_print(f'Parsed args: {self.args}')
|
||||
logger.debug('Initializing Jackify CLI Frontend')
|
||||
logger.debug('JackifyCLI.run() called')
|
||||
logger.debug(f'Parsed args: {self.args}')
|
||||
|
||||
# Handle update functionality
|
||||
if getattr(self.args, 'update', False):
|
||||
self._debug_print('Entering update workflow')
|
||||
logger.debug('Entering update workflow')
|
||||
return self._handle_update()
|
||||
|
||||
# Handle legacy restart-steam functionality (temporary)
|
||||
if getattr(self.args, 'restart_steam', False):
|
||||
self._debug_print('Entering restart_steam workflow')
|
||||
logger.debug('Entering restart_steam workflow')
|
||||
return self._handle_restart_steam()
|
||||
|
||||
|
||||
# Handle install-modlist top-level functionality
|
||||
if getattr(self.args, 'install_modlist', False):
|
||||
self._debug_print('Entering install_modlist workflow')
|
||||
logger.debug('Entering install_modlist workflow')
|
||||
return self.commands['install_modlist'].execute_top_level(self.args)
|
||||
|
||||
# Handle subcommands
|
||||
@@ -514,12 +525,10 @@ class JackifyCLI:
|
||||
command_instance.run()
|
||||
return 0
|
||||
|
||||
|
||||
def main():
|
||||
"""Legacy main function (not used in new structure)"""
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Do not call directly -- use __main__.py
|
||||
print("Please use: python -m jackify.frontends.cli")
|
||||
|
||||
@@ -37,8 +37,10 @@ class AdditionalMenuHandler:
|
||||
print(f" {COLOR_ACTION}→ Install TTW using TTW_Linux_Installer{COLOR_RESET}")
|
||||
print(f"{COLOR_SELECTION}3.{COLOR_RESET} Install Wabbajack Application")
|
||||
print(f" {COLOR_ACTION}→ Downloads and configures the Wabbajack app itself (via Proton){COLOR_RESET}")
|
||||
print(f"{COLOR_SELECTION}4.{COLOR_RESET} Setup Mod Organizer 2")
|
||||
print(f" {COLOR_ACTION}→ Download and configure a standalone MO2 instance{COLOR_RESET}")
|
||||
print(f"{COLOR_SELECTION}0.{COLOR_RESET} Return to Main Menu")
|
||||
selection = input(f"\n{COLOR_PROMPT}Enter your selection (0-3): {COLOR_RESET}").strip()
|
||||
selection = input(f"\n{COLOR_PROMPT}Enter your selection (0-4): {COLOR_RESET}").strip()
|
||||
|
||||
if selection.lower() == 'q': # Allow 'q' to re-display menu
|
||||
continue
|
||||
@@ -48,21 +50,14 @@ class AdditionalMenuHandler:
|
||||
self._execute_ttw_install(cli_instance)
|
||||
elif selection == "3":
|
||||
self._execute_install_wabbajack(cli_instance)
|
||||
elif selection == "4":
|
||||
self._execute_setup_mo2(cli_instance)
|
||||
elif selection == "0":
|
||||
break
|
||||
else:
|
||||
print("Invalid selection. Please try again.")
|
||||
time.sleep(1)
|
||||
|
||||
def _execute_legacy_install_mo2(self, cli_instance):
|
||||
"""LEGACY BRIDGE: Execute MO2 installation"""
|
||||
# LEGACY BRIDGE: Use legacy imports until backend migration complete
|
||||
if hasattr(cli_instance, 'menu') and hasattr(cli_instance.menu, 'mo2_handler'):
|
||||
cli_instance.menu.mo2_handler.install_mo2()
|
||||
else:
|
||||
print(f"{COLOR_INFO}MO2 handler not available - this will be implemented in Phase 2.3{COLOR_RESET}")
|
||||
input("\nPress Enter to continue...")
|
||||
|
||||
def _execute_legacy_recovery_menu(self, cli_instance):
|
||||
"""LEGACY BRIDGE: Execute recovery menu"""
|
||||
# Handled by RecoveryMenuHandler
|
||||
@@ -314,3 +309,12 @@ class AdditionalMenuHandler:
|
||||
if self.logger:
|
||||
self.logger.debug("AdditionalMenuHandler: Executing Install Wabbajack command")
|
||||
command.run()
|
||||
|
||||
def _execute_setup_mo2(self, cli_instance):
|
||||
"""Execute standalone MO2 setup"""
|
||||
from jackify.frontends.cli.commands.setup_mo2 import SetupMO2Command
|
||||
|
||||
command = SetupMO2Command()
|
||||
if self.logger:
|
||||
self.logger.debug("AdditionalMenuHandler: Executing Setup MO2 command")
|
||||
command.run()
|
||||
|
||||
Reference in New Issue
Block a user