From 263ae8e3b4ee39f3b52f20b36c683693c72cee5e Mon Sep 17 00:00:00 2001 From: cproudlock Date: Tue, 4 Aug 2026 12:17:36 -0400 Subject: [PATCH] fix(installer): install the Visual C++ runtime before MySQL, and log the MSI Second failure from the Server 2019 test. The previous fix worked - msiexec went from exit 1639 (ERROR_INVALID_COMMAND_LINE, which is why it printed its usage dialog) to exit 1603 (ERROR_INSTALL_FAILURE), so the command line parses now and the MSI itself is failing. It failed in 1.1 seconds. An MSI that dies that fast has not begun installing; it has failed a launch condition. MySQL 8.4 requires the Visual C++ redistributable and a bare Windows Server does not ship it - the same runtime mysql.exe and mysqldump.exe import, which was visible when their DLL dependencies were trimmed and went unnoticed. Stage 0 now installs VC_redist.x64.exe from the bundle before touching MySQL, skipping it when vcruntime140.dll is already present, and fails with a sentence naming the requirement if the redistributable is absent from the bundle altogether. vcredist\ is an optional locked payload. msiexec also gets /l*v now. A bare 1603 names neither the failing action nor the reason, and it is the most common MySQL install failure - diagnosing this one took a launch-condition inference rather than a log. The MSI log lands beside the installer's own in ProgramData, so the next failure is readable instead of guessed at. --- deploy/windows/installer/README.md | 1 + deploy/windows/installer/build-installer.ps1 | 2 +- deploy/windows/installer/build-installer.sh | 3 +- deploy/windows/installer/bundle-lock.json | 349 +++++++++--------- deploy/windows/installer/bundle-lock.ps1 | 1 + deploy/windows/installer/shopdb-install.ps1 | 33 ++ .../windows/installer/verify_bundle_lock.py | 1 + 7 files changed, 218 insertions(+), 172 deletions(-) diff --git a/deploy/windows/installer/README.md b/deploy/windows/installer/README.md index f40068c..0e50dc4 100644 --- a/deploy/windows/installer/README.md +++ b/deploy/windows/installer/README.md @@ -53,6 +53,7 @@ bundle/wheels/ pip download -r requirements.txt --only-binary=:all bundle/python/ python-3.14.x-amd64.exe bundle/httpplatformhandler/ httpPlatformHandler_amd64.msi bundle/urlrewrite/ rewrite_amd64.msi (client-IP rule; see below) +bundle/vcredist/ VC_redist.x64.exe (MySQL will not install without it) bundle/mysql/ mysql-8.4.x-winx64.msi (bundled-database option only) bundle/mysqlclient/ mysql.exe, mysqldump.exe + libcrypto/libssl (backups) ``` diff --git a/deploy/windows/installer/build-installer.ps1 b/deploy/windows/installer/build-installer.ps1 index 8c098eb..bba59a2 100644 --- a/deploy/windows/installer/build-installer.ps1 +++ b/deploy/windows/installer/build-installer.ps1 @@ -286,7 +286,7 @@ refresh-bundle-lock.ps1, read the diff, and commit the new lock). # --- summary ---------------------------------------------------------------- Write-Host '' Say " Bundle staged at: $BundleRoot" 'White' -foreach ($d in @('app', 'wheels', 'python', 'httpplatformhandler', 'urlrewrite', 'mysqlclient', 'mysql')) { +foreach ($d in @('app', 'wheels', 'python', 'httpplatformhandler', 'urlrewrite', 'vcredist', 'mysqlclient', 'mysql')) { $p = Join-Path $BundleRoot $d if (Test-Path $p) { $mb = ((Get-ChildItem $p -Recurse -File | Measure-Object Length -Sum).Sum / 1MB) diff --git a/deploy/windows/installer/build-installer.sh b/deploy/windows/installer/build-installer.sh index 729a4da..3f527e5 100755 --- a/deploy/windows/installer/build-installer.sh +++ b/deploy/windows/installer/build-installer.sh @@ -82,7 +82,7 @@ done echo "" echo "Bundle staged at: $BUNDLE" -for d in app wheels python httpplatformhandler urlrewrite mysqlclient mysql; do +for d in app wheels python httpplatformhandler urlrewrite vcredist mysqlclient mysql; do if [ -d "$BUNDLE/$d" ]; then printf ' %-20s %s\n' "$d" "$(du -sh "$BUNDLE/$d" | cut -f1)" else @@ -125,6 +125,7 @@ else echo " python/ python-3.14.x-amd64.exe" echo " httpplatformhandler/ httpPlatformHandler_amd64.msi" echo " urlrewrite/ rewrite_amd64.msi (client-IP rule; see README)" + echo " vcredist/ VC_redist.x64.exe (MySQL requires it)" echo " mysql/ mysql-8.4.x-winx64.msi (bundled-database option only)" echo "" echo " If the payload changed ON PURPOSE, regenerate and COMMIT the lock:" diff --git a/deploy/windows/installer/bundle-lock.json b/deploy/windows/installer/bundle-lock.json index cfde5a2..ced25fe 100644 --- a/deploy/windows/installer/bundle-lock.json +++ b/deploy/windows/installer/bundle-lock.json @@ -1,34 +1,84 @@ { "schema": 1, - "generated": "2026-08-04T11:55:25Z", + "generated": "2026-08-04T16:16:57Z", "pythontag": "cp314", "platform": "win_amd64", "note": "Exact third-party payload of the installer bundle. Regenerate with refresh-bundle-lock.ps1 and COMMIT the change as a reviewed dependency bump.", "payloads": { "python": { - "required": true, "files": { "python-3.14.6-amd64.exe": { "sha256": "14b3e9a710a3fcf0bd9b55ab6b60412bd91227563f813fc49040cabc0209e0bd", "size": 30774112 } - } - }, - "urlrewrite": { - "required": false, - "files": { - "rewrite_amd64_en-US.msi": { - "sha256": "37342ff2f585f263f34f48e9de59eb1051d61015a8e967dbde4075716230a32a", - "size": 6078464 - } - } + }, + "required": true }, "wheels": { - "required": true, "files": { - "urllib3-2.7.0-py3-none-any.whl": { - "sha256": "9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", - "size": 131087 + "flask_jwt_extended-4.7.3-py2.py3-none-any.whl": { + "sha256": "905ac807b52b5409bc9244dbcca434968c13ca6f9d91bffe7d4cb71e0e6231cb", + "size": 22698 + }, + "requests-2.33.1-py3-none-any.whl": { + "sha256": "4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a", + "size": 64947 + }, + "waitress-3.0.2-py3-none-any.whl": { + "sha256": "c56d67fd6e87c2ee598b76abdd4e96cfad1f24cacdea5078d382b1f9d7b5ed2e", + "size": 56232 + }, + "tabulate-0.10.0-py3-none-any.whl": { + "sha256": "f0b0622e567335c8fabaaa659f1b33bcb6ddfe2e496071b743aa113f8774f2d3", + "size": 39814 + }, + "click-8.3.3-py3-none-any.whl": { + "sha256": "a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613", + "size": 110502 + }, + "alembic-1.18.4-py3-none-any.whl": { + "sha256": "a5ed4adcf6d8a4cb575f3d759f071b03cd6e5c7618eb796cb52497be25bfe19a", + "size": 263893 + }, + "werkzeug-3.1.8-py3-none-any.whl": { + "sha256": "63a77fb8892bf28ebc3178683445222aa500e48ebad5ec77b0ad80f8726b1f50", + "size": 226459 + }, + "marshmallow-4.3.0-py3-none-any.whl": { + "sha256": "46c4fe6984707e3cbd485dfebbf0a59874f58d695aad05c1668d15e8c6e13b46", + "size": 49148 + }, + "flask_cors-6.0.2-py3-none-any.whl": { + "sha256": "e57544d415dfd7da89a9564e1e3a9e515042df76e12130641ca6f3f2f03b699a", + "size": 13257 + }, + "itsdangerous-2.2.0-py3-none-any.whl": { + "sha256": "c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", + "size": 16234 + }, + "blinker-1.9.0-py3-none-any.whl": { + "sha256": "ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", + "size": 8458 + }, + "cryptography-50.0.0-cp311-abi3-win_amd64.whl": { + "sha256": "bd1c592e4d5974f0d08d4888e432157adba757c66da0246918e43677fafa2d30", + "size": 3840395 + }, + "dnspython-2.8.0-py3-none-any.whl": { + "sha256": "01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", + "size": 331094 + }, + "idna-3.13-py3-none-any.whl": { + "sha256": "892ea0cde124a99ce773decba204c5552b69c3c67ffd5f232eb7696135bc8bb3", + "size": 68629 + }, + "flask-3.1.3-py3-none-any.whl": { + "sha256": "f4bcbefc124291925f1a26446da31a5178f9483862233b23c0c96a20701f670c", + "size": 103424 + }, + "cachelib-0.13.0-py3-none-any.whl": { + "sha256": "8c8019e53b6302967d4e8329a504acf75e7bc46130291d30188a6e4e58162516", + "size": 20914 }, "sqlalchemy-2.0.49-cp314-cp314-win_amd64.whl": { "sha256": "77641d299179c37b89cf2343ca9972c88bb6eef0d5fc504a2f86afd15cd5adf5", @@ -38,190 +88,149 @@ "sha256": "1b96bfe2c4bd825681b7d311ad6d9b7280a091f43e8f63da5729638083cd3bfb", "size": 187937 }, - "pymysql-1.1.3-py3-none-any.whl": { - "sha256": "8164ba62c552f6105f3b11753352d0f16b90d1703ba67d81923d5a8a5d1c5289", - "size": 45356 + "Flask_Migrate-4.1.0-py3-none-any.whl": { + "sha256": "24d8051af161782e0743af1b04a152d007bad9772b2bca67b7ec1e8ceeb3910d", + "size": 21237 }, "pyjwt-2.12.1-py3-none-any.whl": { "sha256": "28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c", "size": 29726 }, - "pycparser-3.0-py3-none-any.whl": { - "sha256": "b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", - "size": 48172 - }, - "flask_jwt_extended-4.7.3-py2.py3-none-any.whl": { - "sha256": "905ac807b52b5409bc9244dbcca434968c13ca6f9d91bffe7d4cb71e0e6231cb", - "size": 22698 - }, - "itsdangerous-2.2.0-py3-none-any.whl": { - "sha256": "c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef", - "size": 16234 - }, - "certifi-2026.4.22-py3-none-any.whl": { - "sha256": "3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a", - "size": 135707 - }, - "flask-3.1.3-py3-none-any.whl": { - "sha256": "f4bcbefc124291925f1a26446da31a5178f9483862233b23c0c96a20701f670c", - "size": 103424 - }, - "marshmallow-4.3.0-py3-none-any.whl": { - "sha256": "46c4fe6984707e3cbd485dfebbf0a59874f58d695aad05c1668d15e8c6e13b46", - "size": 49148 - }, - "markupsafe-3.0.3-cp314-cp314-win_amd64.whl": { - "sha256": "bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", - "size": 15341 - }, - "mysql_connector_python-9.7.0-cp314-cp314-win_amd64.whl": { - "sha256": "5a5abbc152bc28cb2e64a04605ecd9941eff6b0dc5f9528cb84adb873e9a1e49", - "size": 18197576 - }, - "jinja2-3.1.6-py3-none-any.whl": { - "sha256": "85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", - "size": 134899 - }, - "waitress-3.0.2-py3-none-any.whl": { - "sha256": "c56d67fd6e87c2ee598b76abdd4e96cfad1f24cacdea5078d382b1f9d7b5ed2e", - "size": 56232 - }, - "colorama-0.4.6-py2.py3-none-any.whl": { - "sha256": "4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", - "size": 25335 - }, - "cachelib-0.13.0-py3-none-any.whl": { - "sha256": "8c8019e53b6302967d4e8329a504acf75e7bc46130291d30188a6e4e58162516", - "size": 20914 - }, - "requests-2.33.1-py3-none-any.whl": { - "sha256": "4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a", - "size": 64947 - }, - "email_validator-2.3.0-py3-none-any.whl": { - "sha256": "80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", - "size": 35604 - }, - "tabulate-0.10.0-py3-none-any.whl": { - "sha256": "f0b0622e567335c8fabaaa659f1b33bcb6ddfe2e496071b743aa113f8774f2d3", - "size": 39814 - }, - "flask_cors-6.0.2-py3-none-any.whl": { - "sha256": "e57544d415dfd7da89a9564e1e3a9e515042df76e12130641ca6f3f2f03b699a", - "size": 13257 - }, - "flask_caching-2.4.0-py3-none-any.whl": { - "sha256": "d15b8135f055c4f28f6f7dbcf8d36a3de4af1224def975ae6e0b43cbfa684486", - "size": 28727 - }, - "typing_extensions-4.15.0-py3-none-any.whl": { - "sha256": "f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", - "size": 44614 - }, - "flask_marshmallow-1.5.0-py3-none-any.whl": { - "sha256": "99951c77e5654111ed733811c6dc9310bfb4c3688c78a9e76f80b5ae0b2279a6", - "size": 12161 - }, - "werkzeug-3.1.8-py3-none-any.whl": { - "sha256": "63a77fb8892bf28ebc3178683445222aa500e48ebad5ec77b0ad80f8726b1f50", - "size": 226459 - }, - "cryptography-50.0.0-cp311-abi3-win_amd64.whl": { - "sha256": "bd1c592e4d5974f0d08d4888e432157adba757c66da0246918e43677fafa2d30", - "size": 3840395 - }, - "idna-3.13-py3-none-any.whl": { - "sha256": "892ea0cde124a99ce773decba204c5552b69c3c67ffd5f232eb7696135bc8bb3", - "size": 68629 - }, - "blinker-1.9.0-py3-none-any.whl": { - "sha256": "ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", - "size": 8458 - }, - "dnspython-2.8.0-py3-none-any.whl": { - "sha256": "01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", - "size": 331094 - }, "python_dotenv-1.2.2-py3-none-any.whl": { "sha256": "1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", "size": 22101 }, - "Flask_Migrate-4.1.0-py3-none-any.whl": { - "sha256": "24d8051af161782e0743af1b04a152d007bad9772b2bca67b7ec1e8ceeb3910d", - "size": 21237 - }, - "mako-1.3.12-py3-none-any.whl": { - "sha256": "8f61569480282dbf557145ce441e4ba888be453c30989f879f0d652e39f53ea9", - "size": 78521 - }, - "flask_sqlalchemy-3.1.1-py3-none-any.whl": { - "sha256": "4ba4be7f419dc72f4efd8802d69974803c37259dd42f3913b0dcf75c9447e0a0", - "size": 25125 - }, - "click-8.3.3-py3-none-any.whl": { - "sha256": "a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613", - "size": 110502 - }, "marshmallow_sqlalchemy-1.5.0-py3-none-any.whl": { "sha256": "3865232672f3dd38c4d5e4e85fdedce76904200742c3594948a2d11d0af93258", "size": 16582 }, - "greenlet-3.5.0-cp314-cp314-win_amd64.whl": { - "sha256": "3bc59be3945ae9750b9e7d45067d01ae3fe90ea5f9ade99239dabdd6e28a5033", - "size": 239835 + "pycparser-3.0-py3-none-any.whl": { + "sha256": "b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", + "size": 48172 }, - "tzdata-2026.3-py2.py3-none-any.whl": { - "sha256": "dc096730c87af6cab1b171c9d532be840741ff5d459015e7f6947bd7d7e54931", - "size": 348168 + "email_validator-2.3.0-py3-none-any.whl": { + "sha256": "80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", + "size": 35604 + }, + "typing_extensions-4.15.0-py3-none-any.whl": { + "sha256": "f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", + "size": 44614 }, "charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl": { "sha256": "92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", "size": 159634 }, - "alembic-1.18.4-py3-none-any.whl": { - "sha256": "a5ed4adcf6d8a4cb575f3d759f071b03cd6e5c7618eb796cb52497be25bfe19a", - "size": 263893 - } - } - }, - "httpplatformhandler": { - "required": true, - "files": { - "httpPlatformHandler_amd64.msi": { - "sha256": "90f8d4905a0ab4f2c95223b3c79e2807a0b74507747d240e43c4302e8db4b5ef", - "size": 557056 - } - } - }, - "mysqlclient": { - "required": false, - "files": { - "libssl-3-x64.dll": { - "sha256": "e24fcd6c8ec31a14c97f520b5b960ed360c55ddf0935040eff297e511697633e", - "size": 788616 + "flask_marshmallow-1.5.0-py3-none-any.whl": { + "sha256": "99951c77e5654111ed733811c6dc9310bfb4c3688c78a9e76f80b5ae0b2279a6", + "size": 12161 }, - "mysqldump.exe": { - "sha256": "329410b7a8ae3d68d38e6f5b781610770854d3f11b975060e231dae62c20dc46", - "size": 7142528 + "urllib3-2.7.0-py3-none-any.whl": { + "sha256": "9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", + "size": 131087 }, - "libcrypto-3-x64.dll": { - "sha256": "e84ec61fc1b07a3c899ae39c1dc3f38591cdd310c043a388e7ab40a6828aa297", - "size": 5187216 + "flask_caching-2.4.0-py3-none-any.whl": { + "sha256": "d15b8135f055c4f28f6f7dbcf8d36a3de4af1224def975ae6e0b43cbfa684486", + "size": 28727 }, - "mysql.exe": { - "sha256": "bd6880253c1853fc17b1e19129d6e5d9f9c86a2dccf2c7c9e6b878a10baa9770", - "size": 7177336 + "mako-1.3.12-py3-none-any.whl": { + "sha256": "8f61569480282dbf557145ce441e4ba888be453c30989f879f0d652e39f53ea9", + "size": 78521 + }, + "markupsafe-3.0.3-cp314-cp314-win_amd64.whl": { + "sha256": "bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", + "size": 15341 + }, + "certifi-2026.4.22-py3-none-any.whl": { + "sha256": "3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a", + "size": 135707 + }, + "jinja2-3.1.6-py3-none-any.whl": { + "sha256": "85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", + "size": 134899 + }, + "flask_sqlalchemy-3.1.1-py3-none-any.whl": { + "sha256": "4ba4be7f419dc72f4efd8802d69974803c37259dd42f3913b0dcf75c9447e0a0", + "size": 25125 + }, + "greenlet-3.5.0-cp314-cp314-win_amd64.whl": { + "sha256": "3bc59be3945ae9750b9e7d45067d01ae3fe90ea5f9ade99239dabdd6e28a5033", + "size": 239835 + }, + "pymysql-1.1.3-py3-none-any.whl": { + "sha256": "8164ba62c552f6105f3b11753352d0f16b90d1703ba67d81923d5a8a5d1c5289", + "size": 45356 + }, + "tzdata-2026.3-py2.py3-none-any.whl": { + "sha256": "dc096730c87af6cab1b171c9d532be840741ff5d459015e7f6947bd7d7e54931", + "size": 348168 + }, + "mysql_connector_python-9.7.0-cp314-cp314-win_amd64.whl": { + "sha256": "5a5abbc152bc28cb2e64a04605ecd9941eff6b0dc5f9528cb84adb873e9a1e49", + "size": 18197576 + }, + "colorama-0.4.6-py2.py3-none-any.whl": { + "sha256": "4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", + "size": 25335 } - } + }, + "required": true }, "mysql": { - "required": false, "files": { "mysql-8.4.6-winx64.msi": { "sha256": "868885b2e221409f0170729bb30b2b2ce83440a1a4c735d19b3c225b51045762", "size": 135081984 } - } + }, + "required": false + }, + "mysqlclient": { + "files": { + "mysqldump.exe": { + "sha256": "329410b7a8ae3d68d38e6f5b781610770854d3f11b975060e231dae62c20dc46", + "size": 7142528 + }, + "libssl-3-x64.dll": { + "sha256": "e24fcd6c8ec31a14c97f520b5b960ed360c55ddf0935040eff297e511697633e", + "size": 788616 + }, + "mysql.exe": { + "sha256": "bd6880253c1853fc17b1e19129d6e5d9f9c86a2dccf2c7c9e6b878a10baa9770", + "size": 7177336 + }, + "libcrypto-3-x64.dll": { + "sha256": "e84ec61fc1b07a3c899ae39c1dc3f38591cdd310c043a388e7ab40a6828aa297", + "size": 5187216 + } + }, + "required": false + }, + "vcredist": { + "files": { + "VC_redist.x64.exe": { + "sha256": "cc0ff0eb1dc3f5188ae6300faef32bf5beeba4bdd6e8e445a9184072096b713b", + "size": 25635768 + } + }, + "required": false + }, + "httpplatformhandler": { + "files": { + "httpPlatformHandler_amd64.msi": { + "sha256": "90f8d4905a0ab4f2c95223b3c79e2807a0b74507747d240e43c4302e8db4b5ef", + "size": 557056 + } + }, + "required": true + }, + "urlrewrite": { + "files": { + "rewrite_amd64_en-US.msi": { + "sha256": "37342ff2f585f263f34f48e9de59eb1051d61015a8e967dbde4075716230a32a", + "size": 6078464 + } + }, + "required": false } } } diff --git a/deploy/windows/installer/bundle-lock.ps1 b/deploy/windows/installer/bundle-lock.ps1 index 7cb317e..373edd5 100644 --- a/deploy/windows/installer/bundle-lock.ps1 +++ b/deploy/windows/installer/bundle-lock.ps1 @@ -50,6 +50,7 @@ $script:BundlePayloads = @( @{ Name = 'httpplatformhandler'; Required = $true; What = 'the IIS module that launches waitress' } @{ Name = 'urlrewrite'; Required = $false; What = 'IIS URL Rewrite, for the client-IP rule' } @{ Name = 'mysqlclient'; Required = $false; What = 'mysql/mysqldump, for backups against a remote database' } + @{ Name = 'vcredist'; Required = $false; What = 'the Visual C++ runtime MySQL requires' } @{ Name = 'mysql'; Required = $false; What = 'MySQL, for the bundled-database option' } ) diff --git a/deploy/windows/installer/shopdb-install.ps1 b/deploy/windows/installer/shopdb-install.ps1 index b43b85a..5f2b759 100644 --- a/deploy/windows/installer/shopdb-install.ps1 +++ b/deploy/windows/installer/shopdb-install.ps1 @@ -677,9 +677,42 @@ Skip stage 0 and point the installer at the existing server with -DbHost. Write-Log "could not start msiserver: $($_.Exception.Message)" 'WARN' } } + # The Visual C++ runtime, BEFORE MySQL. MySQL's MSI carries a launch + # condition requiring it, and a bare Windows Server does not ship it - + # so on a fresh box the MSI aborts in about a second with the generic + # exit 1603 and no indication of what was missing. That is what a real + # Server 2019 install did. + $vcMarker = Join-Path $env:windir 'system32\vcruntime140.dll' + if (Test-Path $vcMarker) { + Write-Log 'Visual C++ runtime already present' 'OK' + } else { + $vc = Get-ChildItem (Join-Path $BundleRoot 'vcredist') -Filter '*.exe' -ErrorAction SilentlyContinue | + Select-Object -First 1 + if (-not $vc) { + Fail 'the Visual C++ runtime is missing and no redistributable is in the bundle' @' +MySQL will not install without it - its MSI fails with a bare exit 1603. +Add vcredist\VC_redist.x64.exe to the bundle and rebuild, or install the +Microsoft Visual C++ 2015-2022 Redistributable (x64) on this server by hand. +'@ + } + Write-Log "installing $($vc.Name) (MySQL requires it)" + if (-not $WhatIfOnly) { + Invoke-Native $vc.FullName @('/install','/quiet','/norestart') ` + 'Visual C++ runtime' -TimeoutSec 600 + Write-Log 'Visual C++ runtime installed' 'OK' + } + } + # Quiet MSI. INSTALLDIR must have no trailing backslash or the MSI mis-parses it. Write-Log "installing $($msi.Name) (125MB, takes a minute)" + # /l*v: without the MSI's own log, exit 1603 says only "something went + # wrong" - it names neither the action nor the reason, and it is the most + # common MySQL failure code. Costs nothing and turns the next failure + # into something readable. + $msiLog = Join-Path $script:LogDir ('mysql-msi-{0}.log' -f (Get-Date -Format 'yyyyMMdd-HHmmss')) + Write-Log "MSI log: $msiLog" Invoke-Native 'msiexec.exe' @('/i', $msi.FullName, '/quiet', '/norestart', + '/l*v', $msiLog, "INSTALLDIR=$MysqlRoot") 'MySQL MSI' -TimeoutSec 900 } # An installer exit code of 0 does NOT mean it did what you asked - the Python diff --git a/deploy/windows/installer/verify_bundle_lock.py b/deploy/windows/installer/verify_bundle_lock.py index 80634f0..b8d036a 100644 --- a/deploy/windows/installer/verify_bundle_lock.py +++ b/deploy/windows/installer/verify_bundle_lock.py @@ -32,6 +32,7 @@ PAYLOADS = [ ('httpplatformhandler', True, 'the IIS module that launches waitress'), ('urlrewrite', False, 'IIS URL Rewrite, for the client-IP rule'), ('mysqlclient', False, 'mysql/mysqldump, for backups against a remote database'), + ('vcredist', False, 'the Visual C++ runtime MySQL requires'), ('mysql', False, 'MySQL, for the bundled-database option'), ]