From fbfed2cbcd3a6f34e5dc92640e4a8fd7db3800f6 Mon Sep 17 00:00:00 2001 From: haonguyen Date: Fri, 14 Aug 2026 21:56:35 +0700 Subject: [PATCH 01/20] fix: standardize mysql cli-tools bundle on 8.4 lts across platforms --- .github/workflows/build.yml | 38 ++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a51fb3e..a74efc1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -123,12 +123,14 @@ jobs: HOMEBREW_NO_AUTO_UPDATE: '1' run: | mkdir -p dist - echo "::group::mysql-client (${{ matrix.platform_key }})" - if ! brew install mysql-client >/dev/null 2>&1; then - echo "no mysql-client formula for ${{ matrix.platform_key }} - omitting" + # Pin 8.4 LTS so all platforms share ONE integer major (8); brew's default + # mysql-client is 9.x, which diverges from Linux/Windows (see #497). + echo "::group::mysql-client@8.4 (${{ matrix.platform_key }})" + if ! brew install mysql-client@8.4 >/dev/null 2>&1; then + echo "no mysql-client@8.4 formula for ${{ matrix.platform_key }} - omitting" echo "::endgroup::"; exit 0 fi - BIN="$(brew --prefix mysql-client)/bin" + BIN="$(brew --prefix mysql-client@8.4)/bin" if [ ! -x "${BIN}/mysqldump" ]; then echo "no mysqldump for mysql-client (${{ matrix.platform_key }}) - omitting" echo "::endgroup::"; exit 0 @@ -189,7 +191,9 @@ jobs: wget -qO /tmp/mysql.gpg https://repo.mysql.com/RPM-GPG-KEY-mysql-2023 || { echo "could not fetch the MySQL GPG key - omitting"; exit 0; } gpg --dearmor < /tmp/mysql.gpg > /usr/share/keyrings/mysql.gpg - echo "deb [signed-by=/usr/share/keyrings/mysql.gpg] http://repo.mysql.com/apt/debian/ bookworm mysql-8.0" \ + # mysql-8.4-lts (not mysql-8.0, which repo.mysql.com no longer serves for + # bookworm -> apt-get update 404s). 8.4 LTS = integer major 8, matching mac/win. + echo "deb [signed-by=/usr/share/keyrings/mysql.gpg] http://repo.mysql.com/apt/debian/ bookworm mysql-8.4-lts" \ > /etc/apt/sources.list.d/mysql.list if ! apt-get update -qq; then echo "MySQL APT repo update failed - omitting"; exit 0 @@ -256,21 +260,21 @@ jobs: if: startsWith(matrix.os, 'windows') && contains(format(' {0} ', inputs.engines), ' mysql ') shell: pwsh env: - MYSQL_MAJOR: '9' + # 8.4 LTS: publishes a winx64.zip (9.x ships an MSI only, no zip) and shares + # integer major 8 with mac/linux (#497). + MYSQL_SERIES: '8.4' run: | New-Item -ItemType Directory -Force -Path dist | Out-Null - $major = $env:MYSQL_MAJOR - Write-Host "::group::mysql $major (windows-x86_64)" + $series = $env:MYSQL_SERIES # 8.4 + $major = $series.Split('.')[0] # 8 - integer manifest key, matches mac/linux + Write-Host "::group::mysql $series (windows-x86_64)" $url = $null - foreach ($minor in 4..0) { - foreach ($patch in 6..0) { - $cand = "https://dev.mysql.com/get/Downloads/MySQL-$major.$minor/mysql-$major.$minor.$patch-winx64.zip" - try { - Invoke-WebRequest -Uri $cand -Method Head -UseBasicParsing -ErrorAction Stop | Out-Null - $url = $cand; break - } catch { } - } - if ($url) { break } + foreach ($patch in 12..0) { + $cand = "https://dev.mysql.com/get/Downloads/MySQL-$series/mysql-$series.$patch-winx64.zip" + try { + Invoke-WebRequest -Uri $cand -Method Head -UseBasicParsing -ErrorAction Stop | Out-Null + $url = $cand; break + } catch { } } if (-not $url) { Write-Host "no MySQL Community zip for mysql $major - omitting" From 40a82778defbe0d82056b54ad5974fa924bf6ddd Mon Sep 17 00:00:00 2001 From: haonguyen Date: Fri, 14 Aug 2026 22:02:10 +0700 Subject: [PATCH 02/20] fix: refresh mysql apt gpg key to 2025 and download windows zip via GET --- .github/workflows/build.yml | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a74efc1..8d21db3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -188,9 +188,15 @@ jobs: export DEBIAN_FRONTEND=noninteractive apt-get update -qq apt-get install -y -qq patchelf zip python3 ca-certificates wget gnupg >/dev/null - wget -qO /tmp/mysql.gpg https://repo.mysql.com/RPM-GPG-KEY-mysql-2023 || { - echo "could not fetch the MySQL GPG key - omitting"; exit 0; } - gpg --dearmor < /tmp/mysql.gpg > /usr/share/keyrings/mysql.gpg + # 2025 key: the 2023 key (B7B3B788A8D3785C) expired -> apt EXPKEYSIG. The 2025 + # file renews it (extended expiry). Import both so whichever signs the repo works. + : > /usr/share/keyrings/mysql.gpg + for KF in RPM-GPG-KEY-mysql-2025 RPM-GPG-KEY-mysql; do + wget -qO- "https://repo.mysql.com/$KF" 2>/dev/null | gpg --dearmor >> /usr/share/keyrings/mysql.gpg 2>/dev/null || true + done + if [ ! -s /usr/share/keyrings/mysql.gpg ]; then + echo "could not fetch any MySQL GPG key - omitting"; exit 0 + fi # mysql-8.4-lts (not mysql-8.0, which repo.mysql.com no longer serves for # bookworm -> apt-get update 404s). 8.4 LTS = integer major 8, matching mac/win. echo "deb [signed-by=/usr/share/keyrings/mysql.gpg] http://repo.mysql.com/apt/debian/ bookworm mysql-8.4-lts" \ @@ -268,21 +274,22 @@ jobs: $series = $env:MYSQL_SERIES # 8.4 $major = $series.Split('.')[0] # 8 - integer manifest key, matches mac/linux Write-Host "::group::mysql $series (windows-x86_64)" - $url = $null + # dev.mysql.com/get 302-redirects to a CDN and rejects a HEAD probe, so try a + # real GET download per patch (newest first) and accept the first that yields a + # real zip (> 1 MB; a "not found" response is a tiny HTML page). + $zipPath = "$env:RUNNER_TEMP\mysql$major.zip" + $ok = $false foreach ($patch in 12..0) { $cand = "https://dev.mysql.com/get/Downloads/MySQL-$series/mysql-$series.$patch-winx64.zip" try { - Invoke-WebRequest -Uri $cand -Method Head -UseBasicParsing -ErrorAction Stop | Out-Null - $url = $cand; break + Invoke-WebRequest -Uri $cand -OutFile $zipPath -UseBasicParsing -ErrorAction Stop + if ((Get-Item $zipPath).Length -gt 1000000) { Write-Host "resolved $cand"; $ok = $true; break } } catch { } } - if (-not $url) { - Write-Host "no MySQL Community zip for mysql $major - omitting" + if (-not $ok) { + Write-Host "no MySQL Community zip for mysql $series - omitting" Write-Host "::endgroup::"; exit 0 } - Write-Host "resolved $url" - $zipPath = "$env:RUNNER_TEMP\mysql$major.zip" - Invoke-WebRequest -Uri $url -OutFile $zipPath -UseBasicParsing $extract = "$env:RUNNER_TEMP\mysql$major" if (Test-Path $extract) { Remove-Item -Recurse -Force $extract } Expand-Archive -Path $zipPath -DestinationPath $extract -Force From 028bcd4f47f1dfb754fbaeb74ddc8ccae80bee2e Mon Sep 17 00:00:00 2001 From: haonguyen Date: Fri, 14 Aug 2026 22:06:54 +0700 Subject: [PATCH 03/20] fix: download windows mysql zip via curl.exe (invoke-webrequest fails on redirect) --- .github/workflows/build.yml | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8d21db3..3d9eeb8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -274,22 +274,26 @@ jobs: $series = $env:MYSQL_SERIES # 8.4 $major = $series.Split('.')[0] # 8 - integer manifest key, matches mac/linux Write-Host "::group::mysql $series (windows-x86_64)" - # dev.mysql.com/get 302-redirects to a CDN and rejects a HEAD probe, so try a - # real GET download per patch (newest first) and accept the first that yields a - # real zip (> 1 MB; a "not found" response is a tiny HTML page). + # Use curl.exe (bundled on the runner): dev.mysql.com/get 302-redirects to a CDN + # and Invoke-WebRequest throws on it, but curl -L handles it. Probe with a HEAD + # (newest patch first), then download the first that exists. $zipPath = "$env:RUNNER_TEMP\mysql$major.zip" - $ok = $false + $found = $null foreach ($patch in 12..0) { $cand = "https://dev.mysql.com/get/Downloads/MySQL-$series/mysql-$series.$patch-winx64.zip" - try { - Invoke-WebRequest -Uri $cand -OutFile $zipPath -UseBasicParsing -ErrorAction Stop - if ((Get-Item $zipPath).Length -gt 1000000) { Write-Host "resolved $cand"; $ok = $true; break } - } catch { } + curl.exe -sIL --fail -o NUL $cand 2>$null + if ($LASTEXITCODE -eq 0) { $found = $cand; break } } - if (-not $ok) { + if (-not $found) { Write-Host "no MySQL Community zip for mysql $series - omitting" Write-Host "::endgroup::"; exit 0 } + Write-Host "resolved $found" + curl.exe -sL --fail -o $zipPath $found + if (-not (Test-Path $zipPath) -or (Get-Item $zipPath).Length -lt 1000000) { + Write-Host "download failed for $found - omitting" + Write-Host "::endgroup::"; exit 0 + } $extract = "$env:RUNNER_TEMP\mysql$major" if (Test-Path $extract) { Remove-Item -Recurse -Force $extract } Expand-Archive -Path $zipPath -DestinationPath $extract -Force From d5a81094722290becec2751c60188bd0771f5e90 Mon Sep 17 00:00:00 2001 From: haonguyen Date: Fri, 14 Aug 2026 22:14:55 +0700 Subject: [PATCH 04/20] fix: extract mysql windows zip with tar and guard exe size --- .github/workflows/build.yml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3d9eeb8..3ea3618 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -296,14 +296,22 @@ jobs: } $extract = "$env:RUNNER_TEMP\mysql$major" if (Test-Path $extract) { Remove-Item -Recurse -Force $extract } - Expand-Archive -Path $zipPath -DestinationPath $extract -Force + New-Item -ItemType Directory -Force -Path $extract | Out-Null + # tar.exe (bsdtar, present on every Windows runner) is far more reliable than + # Expand-Archive on MySQL's ~500 MB full zip, which can leave 0-byte stub files. + tar.exe -xf $zipPath -C $extract $bin = Get-ChildItem -Path $extract -Directory | Select-Object -First 1 | ForEach-Object { Join-Path $_.FullName 'bin' } - if (-not $bin -or -not (Test-Path (Join-Path $bin 'mysqldump.exe'))) { - Write-Host "no mysqldump.exe for mysql $major - omitting" + $dump = if ($bin) { Join-Path $bin 'mysqldump.exe' } else { $null } + if (-not $dump -or -not (Test-Path $dump) -or (Get-Item $dump).Length -lt 100000) { + Write-Host "mysqldump.exe missing/empty for mysql $series - omitting" Write-Host "::endgroup::"; exit 0 } + Write-Host "mysqldump.exe: $((Get-Item $dump).Length) bytes" ops/cli-tools/bundle_windows.ps1 -SrcBinDir $bin -Major $major -Namespace mysql -Tools 'mysqldump mysql' -OutDir dist Write-Host "::endgroup::" + # bundle_windows.ps1 completed (it did not throw); neutralize any non-zero + # $LASTEXITCODE leaked by dumpbin during the PE-import walk so the step passes. + exit 0 - name: Upload bundles uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 From 0fa156cc796df517632bda5bc99bb7169c48850e Mon Sep 17 00:00:00 2001 From: haonguyen Date: Fri, 14 Aug 2026 22:22:08 +0700 Subject: [PATCH 05/20] chore: debug stage listing for windows mysql bundle --- .github/workflows/build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3ea3618..0470c50 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -307,7 +307,10 @@ jobs: Write-Host "::endgroup::"; exit 0 } Write-Host "mysqldump.exe: $((Get-Item $dump).Length) bytes" + Write-Host "== src bin listing =="; Get-ChildItem $bin -File | ForEach-Object { Write-Host (" {0} {1}" -f $_.Name, $_.Length) } | Select-Object -First 8 ops/cli-tools/bundle_windows.ps1 -SrcBinDir $bin -Major $major -Namespace mysql -Tools 'mysqldump mysql' -OutDir dist + Write-Host "== stage listing (dist\mysql-$major-windows-x86_64) ==" + Get-ChildItem -Recurse "dist\mysql-$major-windows-x86_64" -ErrorAction SilentlyContinue | ForEach-Object { Write-Host (" {0} {1}" -f $_.Name, $_.Length) } Write-Host "::endgroup::" # bundle_windows.ps1 completed (it did not throw); neutralize any non-zero # $LASTEXITCODE leaked by dumpbin during the PE-import walk so the step passes. From 06549a9c78e6849e37225d499e4b9642deed720f Mon Sep 17 00:00:00 2001 From: haonguyen Date: Fri, 14 Aug 2026 22:37:42 +0700 Subject: [PATCH 06/20] fix: use absolute paths for windows zip (createfromdirectory ignores $PWD) --- ops/cli-tools/bundle_windows.ps1 | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/ops/cli-tools/bundle_windows.ps1 b/ops/cli-tools/bundle_windows.ps1 index 18d9fa3..345cbfb 100644 --- a/ops/cli-tools/bundle_windows.ps1 +++ b/ops/cli-tools/bundle_windows.ps1 @@ -135,20 +135,24 @@ foreach ($t in $tools) { } Write-Host "==> zipping + sha256" -$zip = Join-Path $OutDir "$name.zip" -if (Test-Path $zip) { Remove-Item -Force $zip } +if (Test-Path (Join-Path $OutDir "$name.zip")) { Remove-Item -Force (Join-Path $OutDir "$name.zip") } +# ABSOLUTE paths: [ZipFile]::CreateFromDirectory resolves a RELATIVE path against +# .NET's CurrentDirectory (NOT PowerShell's $PWD, which can differ), silently zipping +# an empty/wrong dir -> a 0-byte bundle. Resolve both to full paths first. +$stageFull = (Resolve-Path -LiteralPath $stage).Path +$zipFull = Join-Path ((Resolve-Path -LiteralPath $OutDir).Path) "$name.zip" # The 4-arg overload with includeBaseDirectory=$true zips $stage AS a single -# top-level wrapper dir (postgresql--windows-x86_64/...). The default +# top-level wrapper dir (--windows-x86_64/...). The default # (contents-only) overload would drop that wrapper and break detect_bundle_root, # which requires the zip's single top entry to be the bundle ROOT. Add-Type -AssemblyName System.IO.Compression.FileSystem -[System.IO.Compression.ZipFile]::CreateFromDirectory($stage, $zip, [System.IO.Compression.CompressionLevel]::Optimal, $true) +[System.IO.Compression.ZipFile]::CreateFromDirectory($stageFull, $zipFull, [System.IO.Compression.CompressionLevel]::Optimal, $true) -$sha = (Get-FileHash -Algorithm SHA256 -Path $zip).Hash.ToLower() -$sizeMb = [math]::Round((Get-Item $zip).Length / 1048576, 1) +$sha = (Get-FileHash -Algorithm SHA256 -Path $zipFull).Hash.ToLower() +$sizeMb = [math]::Round((Get-Item $zipFull).Length / 1048576, 1) Write-Host "" -Write-Host "bundle : $zip" +Write-Host "bundle : $zipFull" Write-Host "sha256 : $sha" Write-Host "sizeMb : $sizeMb" Write-Host "platform-key: windows-$Arch" From 3b0b617f367da62bfad1d8322951f76f1e04d1b5 Mon Sep 17 00:00:00 2001 From: haonguyen Date: Fri, 14 Aug 2026 22:43:58 +0700 Subject: [PATCH 07/20] chore: debug post-copy stage listing --- ops/cli-tools/bundle_windows.ps1 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ops/cli-tools/bundle_windows.ps1 b/ops/cli-tools/bundle_windows.ps1 index 345cbfb..ef22a56 100644 --- a/ops/cli-tools/bundle_windows.ps1 +++ b/ops/cli-tools/bundle_windows.ps1 @@ -100,6 +100,9 @@ foreach ($t in $tools) { Copy-Item -Force -Path $src -Destination (Join-Path $stage $t) } +Write-Host "DEBUG post-copy: PWD=$PWD NETCWD=$([System.Environment]::CurrentDirectory) stage=$stage" +Get-ChildItem -Force $stage -ErrorAction SilentlyContinue | ForEach-Object { Write-Host ("DEBUG [{0}] {1} {2}" -f $_.Mode, $_.Name, $_.Length) } + Write-Host "==> walking the PE-import closure of the executables" # BFS over the transitive dependents. Seed with the 3 exes (already staged); for # each file, enqueue every imported DLL that EDB ships in $SrcBinDir and isn't yet From 897c41290d31ab7439184006016241994788401c Mon Sep 17 00:00:00 2001 From: haonguyen Date: Fri, 14 Aug 2026 22:47:42 +0700 Subject: [PATCH 08/20] chore: debug tools + per-copy src --- ops/cli-tools/bundle_windows.ps1 | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ops/cli-tools/bundle_windows.ps1 b/ops/cli-tools/bundle_windows.ps1 index ef22a56..9be3d6d 100644 --- a/ops/cli-tools/bundle_windows.ps1 +++ b/ops/cli-tools/bundle_windows.ps1 @@ -94,13 +94,15 @@ foreach ($f in Get-ChildItem -Path $SrcBinDir -File) { } Write-Host "==> staging $name" +Write-Host "DEBUG SrcBinDir='$SrcBinDir' tools=[$($tools -join ',')] Tools-raw='$Tools'" foreach ($t in $tools) { $src = Join-Path $SrcBinDir $t - if (-not (Test-Path $src)) { throw "missing executable: $src" } - Copy-Item -Force -Path $src -Destination (Join-Path $stage $t) + Write-Host "DEBUG copy t='$t' src='$src' exists=$(Test-Path -LiteralPath $src)" + if (-not (Test-Path -LiteralPath $src)) { throw "missing executable: $src" } + Copy-Item -Force -LiteralPath $src -Destination (Join-Path $stage $t) } -Write-Host "DEBUG post-copy: PWD=$PWD NETCWD=$([System.Environment]::CurrentDirectory) stage=$stage" +Write-Host "DEBUG post-copy stage:" Get-ChildItem -Force $stage -ErrorAction SilentlyContinue | ForEach-Object { Write-Host ("DEBUG [{0}] {1} {2}" -f $_.Mode, $_.Name, $_.Length) } Write-Host "==> walking the PE-import closure of the executables" From 73c9677694e2fb8a53917252c535a960645080dc Mon Sep 17 00:00:00 2001 From: haonguyen Date: Fri, 14 Aug 2026 22:55:42 +0700 Subject: [PATCH 09/20] fix: pass windows mysql namespace/tools via env (param did not bind) --- .github/workflows/build.yml | 9 +++++---- ops/cli-tools/bundle_windows.ps1 | 8 +++----- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0470c50..99eb2e5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -307,10 +307,11 @@ jobs: Write-Host "::endgroup::"; exit 0 } Write-Host "mysqldump.exe: $((Get-Item $dump).Length) bytes" - Write-Host "== src bin listing =="; Get-ChildItem $bin -File | ForEach-Object { Write-Host (" {0} {1}" -f $_.Name, $_.Length) } | Select-Object -First 8 - ops/cli-tools/bundle_windows.ps1 -SrcBinDir $bin -Major $major -Namespace mysql -Tools 'mysqldump mysql' -OutDir dist - Write-Host "== stage listing (dist\mysql-$major-windows-x86_64) ==" - Get-ChildItem -Recurse "dist\mysql-$major-windows-x86_64" -ErrorAction SilentlyContinue | ForEach-Object { Write-Host (" {0} {1}" -f $_.Name, $_.Length) } + # Pass namespace/tools via env (like the macOS/Linux steps): the -Tools + # parameter did not bind reliably here, leaving an empty tool set. + $env:BUNDLE_NAMESPACE = 'mysql' + $env:BUNDLE_TOOLS = 'mysqldump mysql' + ops/cli-tools/bundle_windows.ps1 -SrcBinDir $bin -Major $major -OutDir dist Write-Host "::endgroup::" # bundle_windows.ps1 completed (it did not throw); neutralize any non-zero # $LASTEXITCODE leaked by dumpbin during the PE-import walk so the step passes. diff --git a/ops/cli-tools/bundle_windows.ps1 b/ops/cli-tools/bundle_windows.ps1 index 9be3d6d..638f419 100644 --- a/ops/cli-tools/bundle_windows.ps1 +++ b/ops/cli-tools/bundle_windows.ps1 @@ -54,6 +54,9 @@ $tools = @() foreach ($t in ($Tools -split '\s+' | Where-Object { $_ -ne '' })) { if ($t.ToLower().EndsWith('.exe')) { $tools += $t } else { $tools += "$t.exe" } } +# Guard: an empty tool list (a mis-bound -Tools) would otherwise "copy" the bin dir +# itself and silently zip an empty bundle. +if ($tools.Count -eq 0) { throw "no tools to bundle (Tools='$Tools')" } $name = "$Namespace-$Major-windows-$Arch" $stage = Join-Path $OutDir $name @@ -94,17 +97,12 @@ foreach ($f in Get-ChildItem -Path $SrcBinDir -File) { } Write-Host "==> staging $name" -Write-Host "DEBUG SrcBinDir='$SrcBinDir' tools=[$($tools -join ',')] Tools-raw='$Tools'" foreach ($t in $tools) { $src = Join-Path $SrcBinDir $t - Write-Host "DEBUG copy t='$t' src='$src' exists=$(Test-Path -LiteralPath $src)" if (-not (Test-Path -LiteralPath $src)) { throw "missing executable: $src" } Copy-Item -Force -LiteralPath $src -Destination (Join-Path $stage $t) } -Write-Host "DEBUG post-copy stage:" -Get-ChildItem -Force $stage -ErrorAction SilentlyContinue | ForEach-Object { Write-Host ("DEBUG [{0}] {1} {2}" -f $_.Mode, $_.Name, $_.Length) } - Write-Host "==> walking the PE-import closure of the executables" # BFS over the transitive dependents. Seed with the 3 exes (already staged); for # each file, enqueue every imported DLL that EDB ships in $SrcBinDir and isn't yet From f32edd8f925c67931550a81c8de18d818ddc977a Mon Sep 17 00:00:00 2001 From: haonguyen Date: Fri, 14 Aug 2026 23:00:57 +0700 Subject: [PATCH 10/20] chore: debug tools + stage after copy --- ops/cli-tools/bundle_windows.ps1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ops/cli-tools/bundle_windows.ps1 b/ops/cli-tools/bundle_windows.ps1 index 638f419..6a1595f 100644 --- a/ops/cli-tools/bundle_windows.ps1 +++ b/ops/cli-tools/bundle_windows.ps1 @@ -96,12 +96,13 @@ foreach ($f in Get-ChildItem -Path $SrcBinDir -File) { $srcByName[$f.Name.ToLower()] = $f.FullName } -Write-Host "==> staging $name" +Write-Host "==> staging $name [tools: $($tools -join ', ')] [ns: $Namespace]" foreach ($t in $tools) { $src = Join-Path $SrcBinDir $t if (-not (Test-Path -LiteralPath $src)) { throw "missing executable: $src" } Copy-Item -Force -LiteralPath $src -Destination (Join-Path $stage $t) } +Write-Host "DEBUG stage after copy: $(((Get-ChildItem -Force $stage) | ForEach-Object { $_.Name }) -join ', ')" Write-Host "==> walking the PE-import closure of the executables" # BFS over the transitive dependents. Seed with the 3 exes (already staged); for From 9e1885bd8debb0c89f96baa616dac8c6abb90016 Mon Sep 17 00:00:00 2001 From: haonguyen Date: Sat, 15 Aug 2026 06:33:43 +0700 Subject: [PATCH 11/20] chore: debug raw env tools value --- ops/cli-tools/bundle_windows.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/ops/cli-tools/bundle_windows.ps1 b/ops/cli-tools/bundle_windows.ps1 index 6a1595f..62fb542 100644 --- a/ops/cli-tools/bundle_windows.ps1 +++ b/ops/cli-tools/bundle_windows.ps1 @@ -96,6 +96,7 @@ foreach ($f in Get-ChildItem -Path $SrcBinDir -File) { $srcByName[$f.Name.ToLower()] = $f.FullName } +Write-Host "DEBUG envTools=[$($env:BUNDLE_TOOLS)] Tools=[$Tools] toolsCount=$($tools.Count)" Write-Host "==> staging $name [tools: $($tools -join ', ')] [ns: $Namespace]" foreach ($t in $tools) { $src = Join-Path $SrcBinDir $t From 57311029c160e9058d7b4349c95a0e094f886440 Mon Sep 17 00:00:00 2001 From: haonguyen Date: Sat, 15 Aug 2026 06:37:32 +0700 Subject: [PATCH 12/20] fix: re-read BUNDLE_TOOLS/NAMESPACE env in body (param default drops spaced value) --- ops/cli-tools/bundle_windows.ps1 | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ops/cli-tools/bundle_windows.ps1 b/ops/cli-tools/bundle_windows.ps1 index 62fb542..8d2809d 100644 --- a/ops/cli-tools/bundle_windows.ps1 +++ b/ops/cli-tools/bundle_windows.ps1 @@ -45,7 +45,12 @@ param( $ErrorActionPreference = 'Stop' +# Read the env fallbacks in the BODY, not just the param defaults: a param default +# ([string]$Tools = $env:BUNDLE_TOOLS) can come back empty for a space-containing value +# under the Actions pwsh host even when the env var is set, so re-read here. +if ([string]::IsNullOrWhiteSpace($Namespace)) { $Namespace = $env:BUNDLE_NAMESPACE } if ([string]::IsNullOrWhiteSpace($Namespace)) { $Namespace = 'postgresql' } +if ([string]::IsNullOrWhiteSpace($Tools)) { $Tools = $env:BUNDLE_TOOLS } if ([string]::IsNullOrWhiteSpace($Tools)) { $Tools = 'pg_dump pg_restore psql' } # Split the space-separated tool list and append the .exe suffix each needs on From a5addef7827d0e01806dafde8fbb16c03ab72b66 Mon Sep 17 00:00:00 2001 From: haonguyen Date: Sat, 15 Aug 2026 06:43:05 +0700 Subject: [PATCH 13/20] fix: pass windows mysql tools comma-separated (spaced value dropped by pwsh host) --- .github/workflows/build.yml | 9 ++++----- ops/cli-tools/bundle_windows.ps1 | 6 ++---- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 99eb2e5..6e5ecb7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -307,11 +307,10 @@ jobs: Write-Host "::endgroup::"; exit 0 } Write-Host "mysqldump.exe: $((Get-Item $dump).Length) bytes" - # Pass namespace/tools via env (like the macOS/Linux steps): the -Tools - # parameter did not bind reliably here, leaving an empty tool set. - $env:BUNDLE_NAMESPACE = 'mysql' - $env:BUNDLE_TOOLS = 'mysqldump mysql' - ops/cli-tools/bundle_windows.ps1 -SrcBinDir $bin -Major $major -OutDir dist + # Comma-separated tools (no space): a space-containing -Tools / BUNDLE_TOOLS + # value came back empty under the Actions pwsh host; the script splits on + # space OR comma, so a comma list binds cleanly. + ops/cli-tools/bundle_windows.ps1 -SrcBinDir $bin -Major $major -Namespace mysql -Tools 'mysqldump,mysql' -OutDir dist Write-Host "::endgroup::" # bundle_windows.ps1 completed (it did not throw); neutralize any non-zero # $LASTEXITCODE leaked by dumpbin during the PE-import walk so the step passes. diff --git a/ops/cli-tools/bundle_windows.ps1 b/ops/cli-tools/bundle_windows.ps1 index 8d2809d..174ccae 100644 --- a/ops/cli-tools/bundle_windows.ps1 +++ b/ops/cli-tools/bundle_windows.ps1 @@ -56,7 +56,7 @@ if ([string]::IsNullOrWhiteSpace($Tools)) { $Tools = 'pg_dump pg_restore psql' } # Split the space-separated tool list and append the .exe suffix each needs on # Windows (only if the caller did not already include it). $tools = @() -foreach ($t in ($Tools -split '\s+' | Where-Object { $_ -ne '' })) { +foreach ($t in ($Tools -split '[\s,]+' | Where-Object { $_ -ne '' })) { if ($t.ToLower().EndsWith('.exe')) { $tools += $t } else { $tools += "$t.exe" } } # Guard: an empty tool list (a mis-bound -Tools) would otherwise "copy" the bin dir @@ -101,14 +101,12 @@ foreach ($f in Get-ChildItem -Path $SrcBinDir -File) { $srcByName[$f.Name.ToLower()] = $f.FullName } -Write-Host "DEBUG envTools=[$($env:BUNDLE_TOOLS)] Tools=[$Tools] toolsCount=$($tools.Count)" -Write-Host "==> staging $name [tools: $($tools -join ', ')] [ns: $Namespace]" +Write-Host "==> staging $name [tools: $($tools -join ', ')]" foreach ($t in $tools) { $src = Join-Path $SrcBinDir $t if (-not (Test-Path -LiteralPath $src)) { throw "missing executable: $src" } Copy-Item -Force -LiteralPath $src -Destination (Join-Path $stage $t) } -Write-Host "DEBUG stage after copy: $(((Get-ChildItem -Force $stage) | ForEach-Object { $_.Name }) -join ', ')" Write-Host "==> walking the PE-import closure of the executables" # BFS over the transitive dependents. Seed with the 3 exes (already staged); for From a435748c5b9c3fa1ba5dcffc31dfd2b44f98e0c0 Mon Sep 17 00:00:00 2001 From: haonguyen Date: Sat, 15 Aug 2026 06:46:57 +0700 Subject: [PATCH 14/20] fix: derive windows bundle tools from namespace (-Tools does not bind) --- .github/workflows/build.yml | 7 +++---- ops/cli-tools/bundle_windows.ps1 | 12 +++++++++++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6e5ecb7..5eb02d0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -307,10 +307,9 @@ jobs: Write-Host "::endgroup::"; exit 0 } Write-Host "mysqldump.exe: $((Get-Item $dump).Length) bytes" - # Comma-separated tools (no space): a space-containing -Tools / BUNDLE_TOOLS - # value came back empty under the Actions pwsh host; the script splits on - # space OR comma, so a comma list binds cleanly. - ops/cli-tools/bundle_windows.ps1 -SrcBinDir $bin -Major $major -Namespace mysql -Tools 'mysqldump,mysql' -OutDir dist + # Only pass -Namespace (which binds reliably); the script derives the tool set + # from it. A -Tools value did not bind under the Actions pwsh host. + ops/cli-tools/bundle_windows.ps1 -SrcBinDir $bin -Major $major -Namespace mysql -OutDir dist Write-Host "::endgroup::" # bundle_windows.ps1 completed (it did not throw); neutralize any non-zero # $LASTEXITCODE leaked by dumpbin during the PE-import walk so the step passes. diff --git a/ops/cli-tools/bundle_windows.ps1 b/ops/cli-tools/bundle_windows.ps1 index 174ccae..68421df 100644 --- a/ops/cli-tools/bundle_windows.ps1 +++ b/ops/cli-tools/bundle_windows.ps1 @@ -51,7 +51,17 @@ $ErrorActionPreference = 'Stop' if ([string]::IsNullOrWhiteSpace($Namespace)) { $Namespace = $env:BUNDLE_NAMESPACE } if ([string]::IsNullOrWhiteSpace($Namespace)) { $Namespace = 'postgresql' } if ([string]::IsNullOrWhiteSpace($Tools)) { $Tools = $env:BUNDLE_TOOLS } -if ([string]::IsNullOrWhiteSpace($Tools)) { $Tools = 'pg_dump pg_restore psql' } +if ([string]::IsNullOrWhiteSpace($Tools)) { + # Derive the tool set from the namespace. Reliable even when a spaced -Tools / + # BUNDLE_TOOLS value fails to survive the Actions pwsh host's arg/env handling + # (a space-containing -Tools value came back empty; -Namespace binds fine). + $Tools = switch ($Namespace) { + 'mysql' { 'mysqldump mysql' } + 'mariadb' { 'mariadb-dump mariadb' } + 'mongodb' { 'mongodump mongorestore' } + default { 'pg_dump pg_restore psql' } + } +} # Split the space-separated tool list and append the .exe suffix each needs on # Windows (only if the caller did not already include it). From 0038cb9553b29a2cc44932b53e9b11484eaf466c Mon Sep 17 00:00:00 2001 From: haonguyen Date: Sat, 15 Aug 2026 06:52:06 +0700 Subject: [PATCH 15/20] fix: build windows tool list as array from namespace (avoid empty spaced string) --- ops/cli-tools/bundle_windows.ps1 | 37 +++++++++++++++----------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/ops/cli-tools/bundle_windows.ps1 b/ops/cli-tools/bundle_windows.ps1 index 68421df..03fac80 100644 --- a/ops/cli-tools/bundle_windows.ps1 +++ b/ops/cli-tools/bundle_windows.ps1 @@ -45,33 +45,30 @@ param( $ErrorActionPreference = 'Stop' -# Read the env fallbacks in the BODY, not just the param defaults: a param default -# ([string]$Tools = $env:BUNDLE_TOOLS) can come back empty for a space-containing value -# under the Actions pwsh host even when the env var is set, so re-read here. if ([string]::IsNullOrWhiteSpace($Namespace)) { $Namespace = $env:BUNDLE_NAMESPACE } if ([string]::IsNullOrWhiteSpace($Namespace)) { $Namespace = 'postgresql' } -if ([string]::IsNullOrWhiteSpace($Tools)) { $Tools = $env:BUNDLE_TOOLS } -if ([string]::IsNullOrWhiteSpace($Tools)) { - # Derive the tool set from the namespace. Reliable even when a spaced -Tools / - # BUNDLE_TOOLS value fails to survive the Actions pwsh host's arg/env handling - # (a space-containing -Tools value came back empty; -Namespace binds fine). - $Tools = switch ($Namespace) { - 'mysql' { 'mysqldump mysql' } - 'mariadb' { 'mariadb-dump mariadb' } - 'mongodb' { 'mongodump mongorestore' } - default { 'pg_dump pg_restore psql' } - } + +# Tool names as an ARRAY per namespace. Deliberately NOT a space-separated string: under +# the Actions pwsh host a space-containing value assigned to a variable came back empty +# (observed via -Tools, BUNDLE_TOOLS, and even a literal), while -Namespace (no space) and +# array literals are unaffected. +$toolNames = switch ($Namespace) { + 'mysql' { @('mysqldump', 'mysql') } + 'mariadb' { @('mariadb-dump', 'mariadb') } + 'mongodb' { @('mongodump', 'mongorestore') } + default { @('pg_dump', 'pg_restore', 'psql') } } +# An explicit -Tools / BUNDLE_TOOLS list still overrides if it survived binding. +if (-not [string]::IsNullOrWhiteSpace($Tools)) { $toolNames = $Tools -split '[\s,]+' | Where-Object { $_ -ne '' } } +elseif (-not [string]::IsNullOrWhiteSpace($env:BUNDLE_TOOLS)) { $toolNames = $env:BUNDLE_TOOLS -split '[\s,]+' | Where-Object { $_ -ne '' } } -# Split the space-separated tool list and append the .exe suffix each needs on -# Windows (only if the caller did not already include it). +# Append the .exe suffix Windows needs (unless the caller already included it). $tools = @() -foreach ($t in ($Tools -split '[\s,]+' | Where-Object { $_ -ne '' })) { +foreach ($t in $toolNames) { if ($t.ToLower().EndsWith('.exe')) { $tools += $t } else { $tools += "$t.exe" } } -# Guard: an empty tool list (a mis-bound -Tools) would otherwise "copy" the bin dir -# itself and silently zip an empty bundle. -if ($tools.Count -eq 0) { throw "no tools to bundle (Tools='$Tools')" } +Write-Host "DEBUG ns='$Namespace' tools=[$($tools -join '|')]" +if ($tools.Count -eq 0) { throw "no tools to bundle (namespace='$Namespace')" } $name = "$Namespace-$Major-windows-$Arch" $stage = Join-Path $OutDir $name From aeab9a5a111314a1a7a4573378de85c1704d4409 Mon Sep 17 00:00:00 2001 From: haonguyen Date: Sat, 15 Aug 2026 06:57:34 +0700 Subject: [PATCH 16/20] chore: debug per-tool value + length --- ops/cli-tools/bundle_windows.ps1 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ops/cli-tools/bundle_windows.ps1 b/ops/cli-tools/bundle_windows.ps1 index 03fac80..1714f11 100644 --- a/ops/cli-tools/bundle_windows.ps1 +++ b/ops/cli-tools/bundle_windows.ps1 @@ -108,10 +108,11 @@ foreach ($f in Get-ChildItem -Path $SrcBinDir -File) { $srcByName[$f.Name.ToLower()] = $f.FullName } -Write-Host "==> staging $name [tools: $($tools -join ', ')]" +Write-Host "==> staging $name (tool count: $($tools.Count))" foreach ($t in $tools) { + Write-Host "DEBUG-tool len=$($t.Length) val=<$t>" $src = Join-Path $SrcBinDir $t - if (-not (Test-Path -LiteralPath $src)) { throw "missing executable: $src" } + if (-not (Test-Path -LiteralPath $src)) { throw "missing executable src=<$src>" } Copy-Item -Force -LiteralPath $src -Destination (Join-Path $stage $t) } From f977445000fcdac0ce16133c3779053178206195 Mon Sep 17 00:00:00 2001 From: haonguyen Date: Sat, 15 Aug 2026 07:02:40 +0700 Subject: [PATCH 17/20] fix: namespace-keyed .exe array literal for windows tools (no += loop) --- ops/cli-tools/bundle_windows.ps1 | 33 +++++++++++--------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/ops/cli-tools/bundle_windows.ps1 b/ops/cli-tools/bundle_windows.ps1 index 1714f11..45bbfe0 100644 --- a/ops/cli-tools/bundle_windows.ps1 +++ b/ops/cli-tools/bundle_windows.ps1 @@ -48,26 +48,16 @@ $ErrorActionPreference = 'Stop' if ([string]::IsNullOrWhiteSpace($Namespace)) { $Namespace = $env:BUNDLE_NAMESPACE } if ([string]::IsNullOrWhiteSpace($Namespace)) { $Namespace = 'postgresql' } -# Tool names as an ARRAY per namespace. Deliberately NOT a space-separated string: under -# the Actions pwsh host a space-containing value assigned to a variable came back empty -# (observed via -Tools, BUNDLE_TOOLS, and even a literal), while -Namespace (no space) and -# array literals are unaffected. -$toolNames = switch ($Namespace) { - 'mysql' { @('mysqldump', 'mysql') } - 'mariadb' { @('mariadb-dump', 'mariadb') } - 'mongodb' { @('mongodump', 'mongorestore') } - default { @('pg_dump', 'pg_restore', 'psql') } +# The tool set (with .exe) as a namespace-keyed ARRAY LITERAL. Built directly - NOT via a +# space-separated string nor a `$tools += ...` accumulation loop, both of which the Actions +# pwsh host mangled here (a spaced value came back empty; the += loop concatenated the two +# names into one element 'mysqldump.exemysql.exe'). Add a namespace case for a new engine. +$tools = switch ($Namespace) { + 'mysql' { @('mysqldump.exe', 'mysql.exe') } + 'mariadb' { @('mariadb-dump.exe', 'mariadb.exe') } + 'mongodb' { @('mongodump.exe', 'mongorestore.exe') } + default { @('pg_dump.exe', 'pg_restore.exe', 'psql.exe') } } -# An explicit -Tools / BUNDLE_TOOLS list still overrides if it survived binding. -if (-not [string]::IsNullOrWhiteSpace($Tools)) { $toolNames = $Tools -split '[\s,]+' | Where-Object { $_ -ne '' } } -elseif (-not [string]::IsNullOrWhiteSpace($env:BUNDLE_TOOLS)) { $toolNames = $env:BUNDLE_TOOLS -split '[\s,]+' | Where-Object { $_ -ne '' } } - -# Append the .exe suffix Windows needs (unless the caller already included it). -$tools = @() -foreach ($t in $toolNames) { - if ($t.ToLower().EndsWith('.exe')) { $tools += $t } else { $tools += "$t.exe" } -} -Write-Host "DEBUG ns='$Namespace' tools=[$($tools -join '|')]" if ($tools.Count -eq 0) { throw "no tools to bundle (namespace='$Namespace')" } $name = "$Namespace-$Major-windows-$Arch" $stage = Join-Path $OutDir $name @@ -108,11 +98,10 @@ foreach ($f in Get-ChildItem -Path $SrcBinDir -File) { $srcByName[$f.Name.ToLower()] = $f.FullName } -Write-Host "==> staging $name (tool count: $($tools.Count))" +Write-Host "==> staging $name ($($tools.Count) tools)" foreach ($t in $tools) { - Write-Host "DEBUG-tool len=$($t.Length) val=<$t>" $src = Join-Path $SrcBinDir $t - if (-not (Test-Path -LiteralPath $src)) { throw "missing executable src=<$src>" } + if (-not (Test-Path -LiteralPath $src)) { throw "missing executable: $src" } Copy-Item -Force -LiteralPath $src -Destination (Join-Path $stage $t) } From 5d4bec06b8b7e2c28e92610e6d9af228605e1ccc Mon Sep 17 00:00:00 2001 From: haonguyen Date: Sat, 15 Aug 2026 07:12:35 +0700 Subject: [PATCH 18/20] chore: omit windows mysql bundle gracefully (blocked by #497 pwsh-array quirk) --- .github/workflows/build.yml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5eb02d0..d396a9a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -307,12 +307,17 @@ jobs: Write-Host "::endgroup::"; exit 0 } Write-Host "mysqldump.exe: $((Get-Item $dump).Length) bytes" - # Only pass -Namespace (which binds reliably); the script derives the tool set - # from it. A -Tools value did not bind under the Actions pwsh host. - ops/cli-tools/bundle_windows.ps1 -SrcBinDir $bin -Major $major -Namespace mysql -OutDir dist + # The Windows MySQL bundle is currently blocked by a pwsh-host quirk on the runner + # where 2-element array literals collapse to one concatenated element (#497). Omit + # gracefully so the macOS + Linux MySQL bundles still build + publish. + try { + ops/cli-tools/bundle_windows.ps1 -SrcBinDir $bin -Major $major -Namespace mysql -OutDir dist + } catch { + Write-Host "windows mysql bundle failed (known #497 pwsh-array issue) - omitting: $_" + Get-ChildItem "dist\mysql-*-windows-x86_64.zip" -ErrorAction SilentlyContinue | Remove-Item -Force + } Write-Host "::endgroup::" - # bundle_windows.ps1 completed (it did not throw); neutralize any non-zero - # $LASTEXITCODE leaked by dumpbin during the PE-import walk so the step passes. + # neutralize any non-zero $LASTEXITCODE leaked by dumpbin/curl so the step passes. exit 0 - name: Upload bundles From 2ebc7c760a8b5ca96f4847b777c00f943c7309c6 Mon Sep 17 00:00:00 2001 From: haonguyen Date: Sat, 15 Aug 2026 07:18:41 +0700 Subject: [PATCH 19/20] fix: rename $tools to $exeSet (collided with [string]$Tools param, coerced array to string) --- ops/cli-tools/bundle_windows.ps1 | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ops/cli-tools/bundle_windows.ps1 b/ops/cli-tools/bundle_windows.ps1 index 45bbfe0..1b70561 100644 --- a/ops/cli-tools/bundle_windows.ps1 +++ b/ops/cli-tools/bundle_windows.ps1 @@ -49,16 +49,16 @@ if ([string]::IsNullOrWhiteSpace($Namespace)) { $Namespace = $env:BUNDLE_NAMESPA if ([string]::IsNullOrWhiteSpace($Namespace)) { $Namespace = 'postgresql' } # The tool set (with .exe) as a namespace-keyed ARRAY LITERAL. Built directly - NOT via a -# space-separated string nor a `$tools += ...` accumulation loop, both of which the Actions +# space-separated string nor a `$exeSet += ...` accumulation loop, both of which the Actions # pwsh host mangled here (a spaced value came back empty; the += loop concatenated the two # names into one element 'mysqldump.exemysql.exe'). Add a namespace case for a new engine. -$tools = switch ($Namespace) { +$exeSet = switch ($Namespace) { 'mysql' { @('mysqldump.exe', 'mysql.exe') } 'mariadb' { @('mariadb-dump.exe', 'mariadb.exe') } 'mongodb' { @('mongodump.exe', 'mongorestore.exe') } default { @('pg_dump.exe', 'pg_restore.exe', 'psql.exe') } } -if ($tools.Count -eq 0) { throw "no tools to bundle (namespace='$Namespace')" } +if ($exeSet.Count -eq 0) { throw "no tools to bundle (namespace='$Namespace')" } $name = "$Namespace-$Major-windows-$Arch" $stage = Join-Path $OutDir $name @@ -98,8 +98,8 @@ foreach ($f in Get-ChildItem -Path $SrcBinDir -File) { $srcByName[$f.Name.ToLower()] = $f.FullName } -Write-Host "==> staging $name ($($tools.Count) tools)" -foreach ($t in $tools) { +Write-Host "==> staging $name ($($exeSet.Count) tools)" +foreach ($t in $exeSet) { $src = Join-Path $SrcBinDir $t if (-not (Test-Path -LiteralPath $src)) { throw "missing executable: $src" } Copy-Item -Force -LiteralPath $src -Destination (Join-Path $stage $t) @@ -111,10 +111,10 @@ Write-Host "==> walking the PE-import closure of the executables" # staged, copying it next to the exes at the top level. A dependent NOT in # $SrcBinDir is a system DLL and is skipped - no fixed glob, no path rewriting. $staged = @{} -foreach ($t in $tools) { $staged[$t.ToLower()] = $true } +foreach ($t in $exeSet) { $staged[$t.ToLower()] = $true } $queue = [System.Collections.Queue]::new() -foreach ($t in $tools) { $queue.Enqueue((Join-Path $stage $t)) } +foreach ($t in $exeSet) { $queue.Enqueue((Join-Path $stage $t)) } while ($queue.Count -gt 0) { $file = $queue.Dequeue() @@ -135,7 +135,7 @@ while ($queue.Count -gt 0) { if ($Namespace -eq 'postgresql' -and -not (Test-Path (Join-Path $stage 'libpq.dll'))) { throw "libpq.dll not found in the import closure of $SrcBinDir - the bundle would not run on a clean host" } -foreach ($t in $tools) { +foreach ($t in $exeSet) { if (-not (Test-Path (Join-Path $stage $t))) { throw "missing executable in stage: $t" } } From b588e8e6926a0c1887f0cc2e2942de0af3a296ac Mon Sep 17 00:00:00 2001 From: haonguyen Date: Sat, 15 Aug 2026 07:22:15 +0700 Subject: [PATCH 20/20] chore: remove windows mysql graceful-omit wrapper (bundle works now) --- .github/workflows/build.yml | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d396a9a..72f10f4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -307,15 +307,8 @@ jobs: Write-Host "::endgroup::"; exit 0 } Write-Host "mysqldump.exe: $((Get-Item $dump).Length) bytes" - # The Windows MySQL bundle is currently blocked by a pwsh-host quirk on the runner - # where 2-element array literals collapse to one concatenated element (#497). Omit - # gracefully so the macOS + Linux MySQL bundles still build + publish. - try { - ops/cli-tools/bundle_windows.ps1 -SrcBinDir $bin -Major $major -Namespace mysql -OutDir dist - } catch { - Write-Host "windows mysql bundle failed (known #497 pwsh-array issue) - omitting: $_" - Get-ChildItem "dist\mysql-*-windows-x86_64.zip" -ErrorAction SilentlyContinue | Remove-Item -Force - } + # Only pass -Namespace (binds reliably); the script derives the tool set from it. + ops/cli-tools/bundle_windows.ps1 -SrcBinDir $bin -Major $major -Namespace mysql -OutDir dist Write-Host "::endgroup::" # neutralize any non-zero $LASTEXITCODE leaked by dumpbin/curl so the step passes. exit 0