From 9980dab27b37497dfb5fc64c7b07f6f394eef491 Mon Sep 17 00:00:00 2001 From: 2Shirt <2xShirt@gmail.com> Date: Sun, 25 Jun 2023 02:21:26 -0700 Subject: [PATCH 1/4] Add initial winget support --- scripts/install_winget.ps1 | 37 +++++++++++++++++++++++++++++++++++++ scripts/wk/os/win.py | 36 +++++++++++++++++++++++++++++++++++- 2 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 scripts/install_winget.ps1 diff --git a/scripts/install_winget.ps1 b/scripts/install_winget.ps1 new file mode 100644 index 00000000..72d45659 --- /dev/null +++ b/scripts/install_winget.ps1 @@ -0,0 +1,37 @@ +# WizardKit: Install winget (if needed) + +#Requires -Version 3.0 +if (Test-Path Env:\DEBUG) { + Set-PSDebug -Trace 1 +} +$Host.UI.RawUI.WindowTitle = "WizardKit: Winget installer" +$Host.UI.RawUI.BackgroundColor = "black" +$Host.UI.RawUI.ForegroundColor = "white" +$ProgressPreference = "SilentlyContinue" + +# STATIC VARIABLES +$EXIT_OK = 0 +$EXIT_INSTALLED = 1 +$EXIT_FAILED_TO_INSTALL = 2 + +# Main +$NeedsInstalled = $false +try { + $_ = $(winget --version) +} +catch { + $NeedsInstalled = $true +} + +# Install +if (! $NeedsInstalled) { + exit $EXIT_INSTALLED + } +try { + Add-AppxPackage -ErrorAction Stop -RegisterByFamilyName -MainPackage Microsoft.DesktopAppInstaller_8wekyb3d8bbwe +} +catch { + exit $EXIT_FAILED_TO_INSTALL +} + +exit $EXIT_OK diff --git a/scripts/wk/os/win.py b/scripts/wk/os/win.py index d13e12fe..1e6ea48c 100644 --- a/scripts/wk/os/win.py +++ b/scripts/wk/os/win.py @@ -26,7 +26,7 @@ from wk.cfg.windows_builds import ( OUTDATED_BUILD_NUMBERS, WINDOWS_BUILDS, ) -from wk.exe import get_json_from_command, run_program +from wk.exe import get_json_from_command, run_program, wait_for_procs from wk.kit.tools import find_kit_dir from wk.std import ( GenericError, @@ -744,5 +744,39 @@ def stop_service(service_name) -> None: raise GenericError(f'Failed to stop service {service_name}') +# Winget Functions +def winget_check(raise_exceptions: bool = False) -> None: + """Check if winget is present, install if not.""" + cmd = [ + 'powershell', + '-ExecutionPolicy', 'bypass', + '-File', find_kit_dir('Scripts').joinpath('install_winget.ps1'), + ] + proc = run_program(cmd, check=False) + + # Raise exception if requested + if raise_exceptions: + if proc.returncode == 1: + raise GenericWarning('Already installed') + if proc.returncode == 2: + raise GenericError('Failed to install') + + +def winget_upgrade() -> None: + """Upgrade all supported programs with winget, returns subprocess.Popen.""" + cmd = ['winget', 'upgrade', '--all'] + + # Adjust if running inside ConEmu + tmp_file = fr'{os.environ.get("TMP")}\run_winget.cmd' + if CONEMU: + with open(tmp_file, 'w', encoding='utf-8') as _f: + _f.write('@echo off\n') + _f.write(" ".join(cmd)) + cmd = ('cmd', '/c', tmp_file, '-new_console:n', '-new_console:s33V') + run_program(cmd, check=False, pipe=False) + sleep(1) + wait_for_procs('winget.exe') + + if __name__ == '__main__': print("This file is not meant to be called directly.") From 3ff61e9948dc7cd1760c6662bffe0204c37867e4 Mon Sep 17 00:00:00 2001 From: 2Shirt <2xShirt@gmail.com> Date: Sun, 25 Jun 2023 02:22:04 -0700 Subject: [PATCH 2/4] Add winget import support --- scripts/wk/cfg/sources.py | 8 ------- scripts/wk/cfg/winget/default.json | 32 ++++++++++++++++++++++++++++ scripts/wk/cfg/winget/vcredists.json | 29 +++++++++++++++++++++++++ scripts/wk/os/win.py | 24 +++++++++++++++++++++ 4 files changed, 85 insertions(+), 8 deletions(-) create mode 100644 scripts/wk/cfg/winget/default.json create mode 100644 scripts/wk/cfg/winget/vcredists.json diff --git a/scripts/wk/cfg/sources.py b/scripts/wk/cfg/sources.py index 16235639..88730320 100644 --- a/scripts/wk/cfg/sources.py +++ b/scripts/wk/cfg/sources.py @@ -25,14 +25,6 @@ SOURCES = { 'Software Bundle': 'https://ninite.com/.net4.8-7zip-chrome-edge-vlc/ninite.exe', 'TDSSKiller': 'https://media.kaspersky.com/utilities/VirusUtilities/EN/tdsskiller.exe', - # Visual C++ Runtimes: https://docs.microsoft.com/en-US/cpp/windows/latest-supported-vc-redist - 'VCRedist_2012_x32': 'https://download.microsoft.com/download/1/6/B/16B06F60-3B20-4FF2-B699-5E9B7962F9AE/VSU_4/vcredist_x86.exe', - 'VCRedist_2012_x64': 'https://download.microsoft.com/download/1/6/B/16B06F60-3B20-4FF2-B699-5E9B7962F9AE/VSU_4/vcredist_x64.exe', - 'VCRedist_2013_x32': 'https://aka.ms/highdpimfc2013x86enu', - 'VCRedist_2013_x64': 'https://aka.ms/highdpimfc2013x64enu', - 'VCRedist_2022_x32': 'https://aka.ms/vs/17/release/vc_redist.x86.exe', - 'VCRedist_2022_x64': 'https://aka.ms/vs/17/release/vc_redist.x64.exe', - # Build Kit 'AIDA64': 'https://download.aida64.com/aida64engineer675.zip', 'Adobe Reader DC': 'https://ardownload2.adobe.com/pub/adobe/reader/win/AcrobatDC/2200220191/AcroRdrDC2200220191_en_US.exe', diff --git a/scripts/wk/cfg/winget/default.json b/scripts/wk/cfg/winget/default.json new file mode 100644 index 00000000..a3497a32 --- /dev/null +++ b/scripts/wk/cfg/winget/default.json @@ -0,0 +1,32 @@ +{ + "$schema": "https://aka.ms/winget-packages.schema.2.0.json", + "CreationDate": "2023-06-25T01:40:45.003-00:00", + "Sources": [ + { + "Packages": [ + { + "PackageIdentifier": "7zip.7zip" + }, + { + "PackageIdentifier": "Google.Chrome" + }, + { + "PackageIdentifier": "Microsoft.Edge" + }, + { + "PackageIdentifier": "Mozilla.Firefox" + }, + { + "PackageIdentifier": "VideoLAN.VLC" + } + ], + "SourceDetails": { + "Argument": "https://cdn.winget.microsoft.com/cache", + "Identifier": "Microsoft.Winget.Source_8wekyb3d8bbwe", + "Name": "winget", + "Type": "Microsoft.PreIndexed.Package" + } + } + ], + "WinGetVersion": "1.4.11071" +} diff --git a/scripts/wk/cfg/winget/vcredists.json b/scripts/wk/cfg/winget/vcredists.json new file mode 100644 index 00000000..b638cb4f --- /dev/null +++ b/scripts/wk/cfg/winget/vcredists.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://aka.ms/winget-packages.schema.2.0.json", + "CreationDate": "2023-06-25T01:40:45.003-00:00", + "Sources": [ + { + "Packages": [ + { + "PackageIdentifier": "Microsoft.VCRedist.2013.x64" + }, + { + "PackageIdentifier": "Microsoft.VCRedist.2013.x86" + }, + { + "PackageIdentifier": "Microsoft.VCRedist.2015+.x64" + }, + { + "PackageIdentifier": "Microsoft.VCRedist.2015+.x86" + } + ], + "SourceDetails": { + "Argument": "https://cdn.winget.microsoft.com/cache", + "Identifier": "Microsoft.Winget.Source_8wekyb3d8bbwe", + "Name": "winget", + "Type": "Microsoft.PreIndexed.Package" + } + } + ], + "WinGetVersion": "1.4.11071" +} diff --git a/scripts/wk/os/win.py b/scripts/wk/os/win.py index 1e6ea48c..ad1ed272 100644 --- a/scripts/wk/os/win.py +++ b/scripts/wk/os/win.py @@ -762,6 +762,30 @@ def winget_check(raise_exceptions: bool = False) -> None: raise GenericError('Failed to install') +def winget_import(group_name: str = 'default', upgrade: bool = False) -> None: + """Use winget to import a set of applications. + + group_name should be the name of a JSON file exported from winget. + + NOTE: The path is relative to .bin/Scripts/wk/cfg/winget/ + """ + cmd = [ + 'winget', + 'import', '--import-file', + str(find_kit_dir('Scripts').joinpath(f'wk/cfg/winget/{group_name}.json')), + '' if upgrade else '--no-upgrade', + ] + tmp_file = fr'{os.environ.get("TMP")}\run_winget.cmd' + if CONEMU: + with open(tmp_file, 'w', encoding='utf-8') as _f: + _f.write('@echo off\n') + _f.write(" ".join(cmd)) + cmd = ('cmd', '/c', tmp_file, '-new_console:n', '-new_console:s33V') + run_program(cmd, check=False, pipe=False) + sleep(1) + wait_for_procs('winget.exe') + + def winget_upgrade() -> None: """Upgrade all supported programs with winget, returns subprocess.Popen.""" cmd = ['winget', 'upgrade', '--all'] From cafa2c24fb329ecd1820deabce4c72653ad7edb4 Mon Sep 17 00:00:00 2001 From: 2Shirt <2xShirt@gmail.com> Date: Sun, 25 Jun 2023 02:40:43 -0700 Subject: [PATCH 3/4] Switch to winget where appropriate in Auto Setup NOTE: Winget is not used for Firefox, LibreOffice, or Open Shell. This was done because we need more fine-tuned control of the process. --- scripts/auto_setup.py | 4 ++- scripts/wk/os/win.py | 3 +-- scripts/wk/setup/win.py | 60 +++++++++++------------------------------ 3 files changed, 19 insertions(+), 48 deletions(-) diff --git a/scripts/auto_setup.py b/scripts/auto_setup.py index 839ab1e5..a32898c4 100644 --- a/scripts/auto_setup.py +++ b/scripts/auto_setup.py @@ -33,11 +33,13 @@ BASE_MENUS = { MenuEntry('Set Custom Power Plan', 'auto_set_custom_power_plan'), ), 'Install Software': ( - MenuEntry('Visual C++ Runtimes', 'auto_install_vcredists'), + MenuEntry('Winget', 'auto_install_winget'), MenuEntry('Firefox', 'auto_install_firefox'), MenuEntry('LibreOffice', 'auto_install_libreoffice', selected=False), MenuEntry('Open Shell', 'auto_install_open_shell'), MenuEntry('Software Bundle', 'auto_install_software_bundle'), + MenuEntry('Software Upgrades', 'auto_install_software_upgrades'), + MenuEntry('Visual C++ Runtimes', 'auto_install_vcredists'), ), 'Configure System': ( MenuEntry('Open Shell', 'auto_config_open_shell'), diff --git a/scripts/wk/os/win.py b/scripts/wk/os/win.py index ad1ed272..fa99404c 100644 --- a/scripts/wk/os/win.py +++ b/scripts/wk/os/win.py @@ -762,7 +762,7 @@ def winget_check(raise_exceptions: bool = False) -> None: raise GenericError('Failed to install') -def winget_import(group_name: str = 'default', upgrade: bool = False) -> None: +def winget_import(group_name: str = 'default') -> None: """Use winget to import a set of applications. group_name should be the name of a JSON file exported from winget. @@ -773,7 +773,6 @@ def winget_import(group_name: str = 'default', upgrade: bool = False) -> None: 'winget', 'import', '--import-file', str(find_kit_dir('Scripts').joinpath(f'wk/cfg/winget/{group_name}.json')), - '' if upgrade else '--no-upgrade', ] tmp_file = fr'{os.environ.get("TMP")}\run_winget.cmd' if CONEMU: diff --git a/scripts/wk/setup/win.py b/scripts/wk/setup/win.py index 355e8fd3..42dc6cbe 100644 --- a/scripts/wk/setup/win.py +++ b/scripts/wk/setup/win.py @@ -25,7 +25,6 @@ from wk.exe import kill_procs, run_program, popen_program from wk.io import case_insensitive_path, get_path_obj from wk.kit.tools import ( ARCH, - download_tool, extract_archive, extract_tool, find_kit_dir, @@ -47,6 +46,9 @@ from wk.os.win import ( is_secure_boot_enabled, reg_set_value, reg_write_settings, + winget_check, + winget_import, + winget_upgrade, ) from wk.repairs.win import ( WIDTH, @@ -450,12 +452,22 @@ def auto_install_open_shell() -> None: def auto_install_software_bundle() -> None: """Install standard software bundle.""" - TRY_PRINT.run('Software Bundle...', install_software_bundle) + TRY_PRINT.run('Software Bundle...', winget_import, group_name='default') + + +def auto_install_software_upgrades() -> None: + """Upgrade all supported installed software.""" + TRY_PRINT.run('Software Upgrades...', winget_upgrade) def auto_install_vcredists() -> None: """Install latest supported Visual C++ runtimes.""" - TRY_PRINT.run('Visual C++ Runtimes...', install_vcredists) + TRY_PRINT.run('Visual C++ Runtimes...', winget_import, group_name='vcredists') + + +def auto_install_winget() -> None: + """Install winget if needed.""" + TRY_PRINT.run('Winget...', winget_check, raise_exceptions=True) def auto_open_device_manager() -> None: @@ -786,48 +798,6 @@ def install_open_shell() -> None: run_program(cmd) -def install_software_bundle() -> None: - """Install standard software bundle.""" - download_tool('Ninite', 'Software Bundle') - installer = get_tool_path('Ninite', 'Software Bundle') - msg = 'Waiting for installations to finish...' - warning = 'NOTE: Press CTRL+c to manually resume if it gets stuck...' - - # Start installations and wait for them to finish - ui.print_standard(msg) - ui.print_warning(warning, end='', flush=True) - proc = popen_program([installer]) - try: - proc.wait() - except KeyboardInterrupt: - # Assuming user-forced continue - pass - - # Clear info lines - print( - '\r\033[0K' # Cursor to start of current line and clear to end of line - '\033[F\033[54C' # Cursor to start of prev line and then move 54 right - '\033[0K', # Clear from cursor to end of line - end='', flush=True) - - -def install_vcredists() -> None: - """Install latest supported Visual C++ runtimes.""" - for year in (2012, 2013, 2022): - 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]) - - def uninstall_firefox() -> None: """Uninstall all copies of Firefox.""" json_file = format_log_path(log_name='Installed Programs', timestamp=True) From 9689dcfeab6525c5d7810a40baf25e5312452af4 Mon Sep 17 00:00:00 2001 From: 2Shirt <2xShirt@gmail.com> Date: Sun, 25 Jun 2023 04:10:08 -0700 Subject: [PATCH 4/4] Update source URLs --- scripts/wk/cfg/sources.py | 21 ++++++++++----------- scripts/wk/kit/build_win.py | 8 ++++++-- scripts/wk/setup/win.py | 2 +- setup/windows/build.ps1 | 2 +- setup/windows/sources.json | 9 ++++----- 5 files changed, 22 insertions(+), 20 deletions(-) diff --git a/scripts/wk/cfg/sources.py b/scripts/wk/cfg/sources.py index 88730320..bd807720 100644 --- a/scripts/wk/cfg/sources.py +++ b/scripts/wk/cfg/sources.py @@ -22,12 +22,11 @@ SOURCES = { 'RKill': 'https://download.bleepingcomputer.com/grinler/rkill.exe', 'RegDelNull': 'https://live.sysinternals.com/RegDelNull.exe', 'RegDelNull64': 'https://live.sysinternals.com/RegDelNull64.exe', - 'Software Bundle': 'https://ninite.com/.net4.8-7zip-chrome-edge-vlc/ninite.exe', 'TDSSKiller': 'https://media.kaspersky.com/utilities/VirusUtilities/EN/tdsskiller.exe', # Build Kit 'AIDA64': 'https://download.aida64.com/aida64engineer675.zip', - 'Adobe Reader DC': 'https://ardownload2.adobe.com/pub/adobe/reader/win/AcrobatDC/2200220191/AcroRdrDC2200220191_en_US.exe', + 'Adobe Reader DC': 'https://ardownload2.adobe.com/pub/adobe/reader/win/AcrobatDC/2300320201/AcroRdrDC2300320201_en_US.exe', 'Aria2': 'https://github.com/aria2/aria2/releases/download/release-1.36.0/aria2-1.36.0-win-32bit-build1.zip', 'Autoruns32': 'http://live.sysinternals.com/Autoruns.exe', 'Autoruns64': 'http://live.sysinternals.com/Autoruns64.exe', @@ -35,28 +34,28 @@ SOURCES = { 'BlueScreenView32': 'http://www.nirsoft.net/utils/bluescreenview.zip', 'BlueScreenView64': 'http://www.nirsoft.net/utils/bluescreenview-x64.zip', 'ERUNT': 'http://www.aumha.org/downloads/erunt.zip', - 'Everything32': 'https://www.voidtools.com/Everything-1.4.1.1020.x86.zip', - 'Everything64': 'https://www.voidtools.com/Everything-1.4.1.1020.x64.zip', - 'FastCopy': 'https://ftp.vector.co.jp/75/32/2323/FastCopy4.2.0_installer.exe', + 'Everything32': 'https://www.voidtools.com/Everything-1.4.1.1024.x86.zip', + 'Everything64': 'https://www.voidtools.com/Everything-1.4.1.1024.x64.zip', + 'FastCopy': 'https://download.softrepository.com/files/tools/FastCopy4.2.2_installer.exe', 'Fluent-Metro': 'https://github.com/bonzibudd/Fluent-Metro/releases/download/v1.5.3/Fluent-Metro_1.5.3.zip', 'FurMark': 'https://geeks3d.com/dl/get/696', - 'HWiNFO': 'https://www.sac.sk/download/utildiag/hwi_730.zip', - 'LibreOffice32': 'https://download.documentfoundation.org/libreoffice/stable/7.3.6/win/x86/LibreOffice_7.3.6_Win_x86.msi', - 'LibreOffice64': 'https://download.documentfoundation.org/libreoffice/stable/7.3.6/win/x86_64/LibreOffice_7.3.6_Win_x64.msi', + 'HWiNFO': 'https://www.sac.sk/download/utildiag/hwi_746.zip', + 'LibreOffice32': 'https://download.documentfoundation.org/libreoffice/stable/7.5.4/win/x86/LibreOffice_7.5.4_Win_x86.msi', + 'LibreOffice64': 'https://download.documentfoundation.org/libreoffice/stable/7.5.4/win/x86_64/LibreOffice_7.5.4_Win_x64.msi', 'Macs Fan Control': 'https://www.crystalidea.com/downloads/macsfancontrol_setup.exe', 'Neutron': 'http://keir.net/download/neutron.zip', - 'Notepad++': 'https://github.com/notepad-plus-plus/notepad-plus-plus/releases/download/v8.1.9.3/npp.8.1.9.3.portable.minimalist.7z', + 'Notepad++': 'https://github.com/notepad-plus-plus/notepad-plus-plus/releases/download/v8.5.4/npp.8.5.4.portable.minimalist.7z', 'OpenShell': 'https://github.com/Open-Shell/Open-Shell-Menu/releases/download/v4.4.170/OpenShellSetup_4_4_170.exe', 'PuTTY': 'https://the.earth.li/~sgtatham/putty/latest/w32/putty.zip', 'SDIO Torrent': 'https://www.glenn.delahoy.com/downloads/sdio/SDIO_Update.torrent', 'UninstallView32': 'https://www.nirsoft.net/utils/uninstallview.zip', 'UninstallView64': 'https://www.nirsoft.net/utils/uninstallview-x64.zip', - 'WizTree': 'https://diskanalyzer.com/files/wiztree_4_10_portable.zip', + 'WizTree': 'https://diskanalyzer.com/files/wiztree_4_14_portable.zip', 'XMPlay': 'https://support.xmplay.com/files/20/xmplay385.zip?v=47090', 'XMPlay 7z': 'https://support.xmplay.com/files/16/xmp-7z.zip?v=800962', 'XMPlay Game': 'https://support.xmplay.com/files/12/xmp-gme.zip?v=515637', 'XMPlay RAR': 'https://support.xmplay.com/files/16/xmp-rar.zip?v=409646', - 'XMPlay Innocuous': 'https://support.xmplay.com/files/10/Innocuous%20(v1.5).zip?v=155959', + 'XMPlay Innocuous': 'https://support.xmplay.com/files/10/Innocuous%20(v1.7).zip?v=645281', } diff --git a/scripts/wk/kit/build_win.py b/scripts/wk/kit/build_win.py index cace9c69..82f71299 100644 --- a/scripts/wk/kit/build_win.py +++ b/scripts/wk/kit/build_win.py @@ -237,7 +237,11 @@ def download_libreoffice() -> None: """Download LibreOffice.""" for arch in 32, 64: out_path = INSTALLERS_DIR.joinpath(f'LibreOffice{arch}.msi') - download_file(out_path, SOURCES[f'LibreOffice{arch}']) + download_file( + out_path, + SOURCES[f'LibreOffice{arch}'], + referer='https://www.libreoffice.org/download/download-libreoffice/', + ) ui.sleep(1) @@ -383,7 +387,7 @@ def download_xmplay() -> None: args = [archive, BIN_DIR.joinpath('XMPlay/plugins')] if archive.name == 'Innocuous.zip': args.append( - 'Innocuous (v1.5)/Innocuous (Hue Shifted)/' + 'Innocuous (v1.7)/Innocuous (Hue Shifted)/' 'Innocuous (Dark Skies - Purple-80) [L1].xmpskin' ) extract_archive(*args, mode='e') diff --git a/scripts/wk/setup/win.py b/scripts/wk/setup/win.py index 42dc6cbe..e0894e01 100644 --- a/scripts/wk/setup/win.py +++ b/scripts/wk/setup/win.py @@ -410,7 +410,7 @@ def auto_config_browsers() -> None: 'Set default browser...', set_default_browser, msg_good='STARTED', ) print(prompt, end='', flush=True) - ui.pause('') + ui.pause(' ') # Move cursor to beginning of the previous line and clear prompt print(f'\033[F\r{" "*len(prompt)}\r', end='', flush=True) diff --git a/setup/windows/build.ps1 b/setup/windows/build.ps1 index b64c1bb0..752fabc7 100644 --- a/setup/windows/build.ps1 +++ b/setup/windows/build.ps1 @@ -86,7 +86,7 @@ if ($MyInvocation.InvocationName -ne ".") { $DownloadErrors = 0 # 7-Zip - DownloadFile -Path $Temp -Name "7z-installer.msi" -Url $Sources.'7-Zip Installer' + DownloadFile -Path $Temp -Name "7z-installer.msi" -Url $Sources.'7-Zip' # ConEmu DownloadFile -Path $Temp -Name "ConEmuPack.7z" -Url $Sources.'ConEmu' diff --git a/setup/windows/sources.json b/setup/windows/sources.json index 43b9b249..29e67152 100644 --- a/setup/windows/sources.json +++ b/setup/windows/sources.json @@ -1,7 +1,6 @@ { - "7-Zip Extra": "https://7-zip.org/a/7z2201-extra.7z", - "7-Zip Installer": "https://7-zip.org/a/7z2201.msi", - "ConEmu": "https://github.com/Maximus5/ConEmu/releases/download/v22.08.07/ConEmuPack.220807.7z", - "Python x32": "https://www.python.org/ftp/python/3.10.7/python-3.10.7-embed-win32.zip", - "Python x64": "https://www.python.org/ftp/python/3.10.7/python-3.10.7-embed-amd64.zip" + "7-Zip": "https://7-zip.org/a/7z2301.msi", + "ConEmu": "https://github.com/Maximus5/ConEmu/releases/download/v22.12.18/ConEmuPack.221218.7z", + "Python x32": "https://www.python.org/ftp/python/3.10.11/python-3.10.11-embed-win32.zip", + "Python x64": "https://www.python.org/ftp/python/3.10.11/python-3.10.11-embed-amd64.zip" }