@echo off REM One-time provisioning, auto-run by dockur/windows during the final step REM of Windows's own unattended setup (see its README's /oem mechanism). REM Everything here happens exactly once and lands on the VM's persistent REM disk -- later builds just boot this already-provisioned VM and run REM build.bat, no re-provisioning. REM REM UNTESTED end to end: written from MSYS2's documented CI bootstrap REM sequence (the same one msys2/setup-msys2 uses) and WiX's own docs, not REM verified against a live dockur/windows boot. Expect to debug this on REM the actual first run -- watch it happen at http://localhost:8006. REM REM Every step also echoes to Z:\install_progress.log (best-effort, only REM if the Z:\ shared drive happens to be up already at this point in REM setup) purely so build_windows.sh on the host has SOMETHING to show REM besides silence during the one-time provisioning run. setlocal enabledelayedexpansion call :log "starting FEnigma build-VM provisioning" REM -- Stage the OTHER oem/ files somewhere that outlives C:\OEM itself, REM done first, before anything else. Confirmed on a real run: C:\OEM REM (dockur's /oem copy target) does NOT reliably persist once Windows REM Setup finishes and you're at the desktop -- it's fundamentally a REM Windows Setup-time staging mechanism ($OEM$ folders, copied by WinPE REM "right after the Windows image is applied ... and before the first REM reboot" per Microsoft's own docs), not guaranteed permanent storage, REM and in practice `dir C:\OEM` came back "File Not Found" once actually REM checked from an interactive desktop session. build.bat/product.wxs/ REM watch_build.bat all get referenced again AFTER install.bat's own REM process has exited (by the Startup-folder entry below, potentially REM much later), so they need a home install.bat itself controls and REM knows persists -- a plain folder on C:, not the OEM staging area. mkdir C:\FenigmaBuild 2>nul copy /y C:\OEM\build.bat C:\FenigmaBuild\build.bat >> C:\OEM\install.log 2>&1 copy /y C:\OEM\product.wxs C:\FenigmaBuild\product.wxs >> C:\OEM\install.log 2>&1 copy /y C:\OEM\watch_build.bat C:\FenigmaBuild\watch_build.bat >> C:\OEM\install.log 2>&1 REM -- MSYS2: the "base" self-extracting archive, not the GUI installer -- REM (the GUI installer has no reliable non-interactive/silent flag across REM versions; the base sfx archive is what CI pipelines actually use). REM Discover the current filename by scraping the repo listing, since it's REM datestamped and there's no stable "latest" URL. call :log "finding current MSYS2 base archive..." powershell -NoProfile -Command ^ "$ProgressPreference='SilentlyContinue';" ^ "$html = Invoke-WebRequest -Uri 'https://repo.msys2.org/distrib/x86_64/' -UseBasicParsing;" ^ "$name = ($html.Links | Where-Object { $_.href -match '^msys2-base-x86_64-.*\.sfx\.exe$' } | Select-Object -Last 1).href;" ^ "Invoke-WebRequest -Uri ('https://repo.msys2.org/distrib/x86_64/' + $name) -OutFile 'C:\msys2-base.sfx.exe' -UseBasicParsing" if not exist C:\msys2-base.sfx.exe ( call :log "FAILED: could not download MSYS2 base archive" exit /b 1 ) call :log "extracting MSYS2 to C:\msys64 ..." C:\msys2-base.sfx.exe -y -oC:\ >> C:\OEM\install.log 2>&1 del C:\msys2-base.sfx.exe REM First bash launch finalizes the base install and kills itself off REM mid-update (documented MSYS2 behavior) -- run it, ignore its exit REM code, then run the real update. call :log "bootstrapping MSYS2 (pacman -Syuu, twice) ..." C:\msys64\usr\bin\bash.exe -lc "exit 0" >> C:\OEM\install.log 2>&1 C:\msys64\usr\bin\bash.exe -lc "pacman -Syuu --noconfirm" >> C:\OEM\install.log 2>&1 C:\msys64\usr\bin\bash.exe -lc "pacman -Syuu --noconfirm" >> C:\OEM\install.log 2>&1 call :log "installing GTK4/libadwaita/PyGObject/build deps ..." REM mingw-w64-x86_64-opencv is the C++ library ONLY -- confirmed live on a REM real VM that `import cv2` fails without it, the actual Python bindings REM are the separate mingw-w64-x86_64-python-opencv package. Don't try to REM paper over a missing one with `pip install opencv-python-headless` REM either: MSYS2's mingw64 Python uses a different ABI than PyPI's Windows REM wheels (cp3XX-mingw_x86_64_msvcrt_gnu vs win_amd64), so pip can never REM use a prebuilt wheel there, only build from source, which then needs a REM full separate native toolchain (ninja/cmake/gcc) this VM doesn't have. C:\msys64\usr\bin\bash.exe -lc "pacman -S --noconfirm --needed mingw-w64-x86_64-python mingw-w64-x86_64-python-pip mingw-w64-x86_64-python-gobject mingw-w64-x86_64-gtk4 mingw-w64-x86_64-libadwaita mingw-w64-x86_64-python-numpy mingw-w64-x86_64-python-pillow mingw-w64-x86_64-opencv mingw-w64-x86_64-python-opencv mingw-w64-x86_64-tesseract-ocr" >> C:\OEM\install.log 2>&1 call :log "pip install pytesseract (pure python, no wheel needed) ..." REM --break-system-packages: MSYS2's mingw64 Python enforces PEP 668 REM ("externally-managed-environment"), confirmed live -- a plain REM `pip install` here fails outright without this flag. Safe here: this REM VM's whole mingw64 Python install exists only to run FEnigma, there's REM no system package manager relying on it staying untouched. C:\msys64\mingw64\bin\python3.exe -m pip install --break-system-packages pytesseract >> C:\OEM\install.log 2>&1 REM -- WiX v3 toolset (candle/light/heat), a plain zip of standalone exes, REM no installer needed. Fixed versioned URL, no scraping required. call :log "fetching WiX v3.11 ..." powershell -NoProfile -Command ^ "$ProgressPreference='SilentlyContinue';" ^ "Invoke-WebRequest -Uri 'https://github.com/wixtoolset/wix3/releases/download/wix3111rtm/wix311-binaries.zip' -OutFile 'C:\wix311-binaries.zip' -UseBasicParsing;" ^ "Expand-Archive -Path 'C:\wix311-binaries.zip' -DestinationPath 'C:\wix' -Force" del C:\wix311-binaries.zip REM -- Register the build watcher to run at every login from here on, REM plus kick it off right now too (a fresh login won't retroactively REM fire for this already-logged-in session). Deliberately an All-Users REM Startup-folder entry, NOT a SYSTEM-context Scheduled Task: confirmed REM on a real run that a /ru SYSTEM task can't see Z:\ at all and spins REM forever on watch_build.bat's own "if not exist Z:\" wait -- Z:\ (the REM /shared mount) is mapped per INTERACTIVE session, invisible to a REM SYSTEM task with no session of its own. Startup-folder entries run REM in whichever user's session actually logs in, inheriting their REM drive mappings correctly. call :log "registering build watcher (Startup folder) ..." copy /y C:\FenigmaBuild\watch_build.bat "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp\FenigmaBuildWatcher.bat" >> C:\OEM\install.log 2>&1 start "" cmd /c C:\FenigmaBuild\watch_build.bat call :log "provisioning done" echo DONE > C:\OEM\provisioned.marker if exist Z:\ echo DONE > Z:\PROVISIONED endlocal exit /b 0 :log echo [install.bat] %~1 >> C:\OEM\install.log if exist Z:\ echo [install.bat] %~1 >> Z:\install_progress.log exit /b 0