WizardKit/scripts/wk/setup/win.py
2021-09-15 09:56:09 -06:00

302 lines
7.3 KiB
Python

"""WizardKit: Setup - Windows"""
# vim: sts=2 sw=2 ts=2
import logging
import os
import platform
import re
import sys
import time
from subprocess import CalledProcessError, DEVNULL
from wk.cfg.main import KIT_NAME_FULL, KIT_NAME_SHORT
from wk.exe import (
get_procs,
run_program,
popen_program,
wait_for_procs,
)
from wk.io import (
delete_folder,
get_path_obj,
non_clobber_path,
rename_item,
)
from wk.kit.tools import (
ARCH,
download_tool,
extract_tool,
get_tool_path,
run_tool,
)
from wk.log import format_log_path, update_log_path
from wk.os.win import (
reg_delete_value,
reg_read_value,
reg_set_value,
reg_write_settings,
disable_service,
enable_service,
stop_service,
)
from wk.repairs.win import (
auto_backup_registry,
auto_backup_browser_profiles,
auto_backup_power_plans,
auto_reset_power_plans,
auto_set_custom_power_plan,
auto_enable_regback,
auto_system_restore_enable,
auto_system_restore_set_size,
auto_system_restore_create,
auto_windows_updates_enable,
)
from wk.std import (
GenericError,
GenericWarning,
Menu,
TryAndPrint,
abort,
ask,
clear_screen,
color_string,
pause,
print_info,
print_standard,
print_warning,
set_title,
show_data,
sleep,
strip_colors,
)
# STATIC VARIABLES
LOG = logging.getLogger(__name__)
CONEMU_EXE = get_tool_path('ConEmu', 'ConEmu', check=False)
IN_CONEMU = 'ConEmuPID' in os.environ
MENU_PRESETS = Menu()
PROGRAMFILES_32 = os.environ.get(
'PROGRAMFILES(X86)', os.environ.get(
'PROGRAMFILES', r'C:\Program Files (x86)',
),
)
OS_VERSION = float(platform.win32_ver()[0])
SYSTEMDRIVE = os.environ.get('SYSTEMDRIVE', 'C:')
WIDTH = 50
TRY_PRINT = TryAndPrint()
TRY_PRINT.width = WIDTH
TRY_PRINT.verbose = True
for error in ('CalledProcessError', 'FileNotFoundError'):
TRY_PRINT.add_error(error)
# Auto Setup
def build_menus(base_menus, title, presets):
"""Build menus, returns dict."""
menus = {}
menus['Main'] = Menu(title=f'{title}\n{color_string("Main Menu", "GREEN")}')
# Main Menu
for entry in base_menus['Actions']:
menus['Main'].add_action(entry.name, entry.details)
for group in base_menus['Groups']:
menus['Main'].add_option(group, {'Selected': True})
# Run groups
for group, entries in base_menus['Groups'].items():
menus[group] = Menu(title=f'{title}\n{color_string(group, "GREEN")}')
for entry in entries:
menus[group].add_option(entry.name, entry.details)
menus[group].add_action('All')
menus[group].add_action('None')
menus[group].add_action('Main Menu', {'Separator': True})
menus[group].add_action('Quit')
# Initialize main menu display names
menus['Main'].update()
# Fix Function references
for group, menu in menus.items():
if group not in base_menus['Groups']:
continue
for name in menu.options:
_function = menu.options[name]['Function']
if isinstance(_function, str):
menu.options[name]['Function'] = getattr(
sys.modules[__name__], _function,
)
# Update presets Menu
MENU_PRESETS.title = f'{title}\n{color_string("Load Preset", "GREEN")}'
MENU_PRESETS.add_option('Default')
for name in presets:
MENU_PRESETS.add_option(name)
MENU_PRESETS.add_action('Main Menu')
MENU_PRESETS.add_action('Quit')
MENU_PRESETS.update()
# Done
return menus
def load_preset(menus, presets):
"""Load menu settings from preset."""
selection = MENU_PRESETS.simple_select()
# Exit early
if 'Main Menu' in selection:
return
if 'Quit' in selection:
raise SystemExit
# Default case
if 'Default' in selection:
for menu in menus.values():
for name in menu.options:
menu.options[name]['Selected'] = True
return
# Load preset
preset = presets[selection[0]]
for group, menu in menus.items():
group_enabled = group in preset
for name in menu.options:
value = group_enabled and name in preset[group]
menu.options[name]['Selected'] = value
def run_auto_setup(base_menus, presets):
"""Run Auto Setup."""
update_log_path(dest_name='Auto Setup', timestamp=True)
title = f'{KIT_NAME_FULL}: Auto Setup'
clear_screen()
set_title(title)
print_info(title)
print('')
# Generate menus
print_standard('Initializing...')
menus = build_menus(base_menus, title, presets)
# Show Menu
show_main_menu(base_menus, menus, presets)
# Start setup
clear_screen()
print_standard(title)
print('')
print_info('Running setup')
# Run setup
for group, menu in menus.items():
if group in ('Main', 'Options'):
continue
try:
run_group(group, menu)
except KeyboardInterrupt:
abort()
# Done
print_info('Done')
pause('Press Enter to exit...')
def run_group(group, menu):
"""Run entries in group if appropriate."""
print_info(f' {group}')
for name, details in menu.options.items():
name_str = strip_colors(name)
# Not selected
if not details.get('Selected', False):
show_data(f'{name_str}...', 'Skipped', 'YELLOW', width=WIDTH)
continue
# Selected
details['Function'](group, name)
def show_main_menu(base_menus, menus, presets):
"""Show main menu and handle actions."""
while True:
update_main_menu(menus)
selection = menus['Main'].simple_select(update=False)
if selection[0] in base_menus['Groups'] or selection[0] == 'Options':
show_sub_menu(menus[selection[0]])
if selection[0] == 'Load Preset':
load_preset(menus, presets)
elif 'Start' in selection:
break
elif 'Quit' in selection:
raise SystemExit
def show_sub_menu(menu):
"""Show sub-menu and handle sub-menu actions."""
while True:
selection = menu.advanced_select()
if 'Main Menu' in selection:
break
if 'Quit' in selection:
raise SystemExit
# Select all or none
value = 'All' in selection
for name in menu.options:
if not menu.options[name].get('Disabled', False):
menu.options[name]['Selected'] = value
def update_main_menu(menus):
"""Update main menu based on current selections."""
index = 1
skip = 'Reboot'
for name in menus['Main'].options:
checkmark = ' '
selected = [
_v['Selected'] for _k, _v in menus[name].options.items() if _k != skip
]
if all(selected):
checkmark = ''
elif any(selected):
checkmark = '-'
display_name = f' {index}: [{checkmark}] {name}'
index += 1
menus['Main'].options[name]['Display Name'] = display_name
# Auto Setup: Wrapper Functions
def auto_install_vcredists():
"""Install latest supported Visual C++ runtimes."""
TRY_PRINT.run('Visual C++ Runtimes...', install_vcredists)
# Install Functions
def install_vcredists():
"""Install latest supported Visual C++ runtimes."""
for year in (2012, 2013, 2019):
cmd_args = ['/install', '/passive', '/norestart']
if year == 2012:
cmd_args.pop(0)
name = f'VCRedist_{year}_x32'
download_tool('VCRedist', name)
installer = get_tool_path('VCRedist', name)
run_program([installer, *cmd_args])
if ARCH == '64':
name = f'{name[:-2]}64'
download_tool('VCRedist', name)
installer = get_tool_path('VCRedist', name)
run_program([installer, *cmd_args])
# Misc Functions
## TODO?
# Tool Functions
## TODO?
# OS Built-in Functions
## TODO?
if __name__ == '__main__':
print("This file is not meant to be called directly.")