From e21db3cc71eff129f4f2bf9b47142fe34ffb839c Mon Sep 17 00:00:00 2001 From: yeshan333 Date: Sat, 23 May 2026 14:51:09 +0800 Subject: [PATCH 01/15] feat: add optional windows luabinaries installer --- hooks/post_install.lua | 44 +++++++++++++++++++++++++++++++++ hooks/pre_install.lua | 16 +++++++++++- lib/utils.lua | 55 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+), 1 deletion(-) diff --git a/hooks/post_install.lua b/hooks/post_install.lua index fb5834d..5c37f47 100644 --- a/hooks/post_install.lua +++ b/hooks/post_install.lua @@ -1,3 +1,5 @@ +local Utils = require("utils") + --- Extension point, called after PreInstall, can perform additional operations, --- such as file operations for the SDK installation directory or compile source code function CheckBuildTools(osType) @@ -22,6 +24,43 @@ Make sure make.exe and gcc.exe are in the System $env:PATH in PowerShell. end end +local function InstallWindowsLuaBinaries(path, lua_version) + local package = Utils.get_windows_luabinaries_package(lua_version) + if package == nil then + error("LuaBinaries package metadata missing for version " .. lua_version) + end + + local cmd = string.format([=[ +powershell -Command " +$ErrorActionPreference = 'Stop'; +$installDir = '%s'; +$binDir = Join-Path $installDir 'bin'; +New-Item -ItemType Directory -Force -Path $binDir | Out-Null; +$luaExe = Join-Path $installDir '%s.exe'; +$luacExe = Join-Path $installDir 'luac%s.exe'; +$wluaExe = Join-Path $installDir '%s.exe'; +$luaDll = Join-Path $installDir '%s'; +if (-not (Test-Path $luaExe)) { throw 'LuaBinaries executable package layout is unexpected.' } +Copy-Item -Path $luaExe -Destination (Join-Path $binDir 'lua.exe') -Force; +if (Test-Path $luacExe) { Copy-Item -Path $luacExe -Destination (Join-Path $binDir 'luac.exe') -Force; } +if (Test-Path $wluaExe) { Copy-Item -Path $wluaExe -Destination (Join-Path $binDir 'wlua.exe') -Force; } +if (Test-Path $luaDll) { Copy-Item -Path $luaDll -Destination (Join-Path $binDir '%s') -Force; } +" +]=], + path, + package.executable_prefix, + string.sub(package.executable_prefix, 4), + package.wlua_prefix, + package.dll_name, + package.dll_name + ) + + local status = os.execute(cmd) + if status ~= 0 then + error("failed to prepare LuaBinaries files for Windows") + end +end + function PLUGIN:PostInstall(ctx) --- ctx.rootPath SDK installation directory @@ -32,6 +71,11 @@ function PLUGIN:PostInstall(ctx) local lua_version = sdkInfo.version print(string.format("os type: %s, lua installed path: %s", RUNTIME.osType, path)) + if RUNTIME.osType == "windows" and Utils.use_windows_luabinaries() then + InstallWindowsLuaBinaries(path, lua_version) + return + end + CheckBuildTools(RUNTIME.osType) -- TODO: support install luajit local status diff --git a/hooks/pre_install.lua b/hooks/pre_install.lua index 77462f8..8b4b345 100644 --- a/hooks/pre_install.lua +++ b/hooks/pre_install.lua @@ -13,6 +13,20 @@ function PLUGIN:PreInstall(ctx) local lua_version = ctx.version local download_url + if RUNTIME.osType == "windows" and Utils.use_windows_luabinaries() then + local package = Utils.get_windows_luabinaries_package(lua_version) + if package == nil then + error("LuaBinaries does not provide a Windows binary for version " .. + lua_version .. ". Disable VFOX_LUA_WINDOWS_LUABINARIES or choose one of: 5.5.0, 5.4.8, 5.3.6, 5.2.4.") + end + + print("lua download url: " .. package.url) + return { + version = lua_version, + url = package.url, + } + end + local v, checksum = Utils.get_version_info(lua_version) if not v then error("Version " .. lua_version .. " not found in https://www.lua.org/ftp/.") @@ -27,4 +41,4 @@ function PLUGIN:PreInstall(ctx) url = download_url, sha256 = checksum } -end \ No newline at end of file +end diff --git a/lib/utils.lua b/lib/utils.lua index 56f7e88..294f266 100644 --- a/lib/utils.lua +++ b/lib/utils.lua @@ -1,6 +1,36 @@ local http = require("http") local lua_utils = {} +local windows_luabinaries_packages = { + ["5.5.0"] = { + archive_name = "lua-5.5.0_Win64_bin.zip", + relative_path = "5.5.0/Tools%20Executables/lua-5.5.0_Win64_bin.zip", + executable_prefix = "lua55", + wlua_prefix = "wlua55", + dll_name = "lua55.dll", + }, + ["5.4.8"] = { + archive_name = "lua-5.4.8_Win64_bin.zip", + relative_path = "5.4.8/Tools%20Executables/lua-5.4.8_Win64_bin.zip", + executable_prefix = "lua54", + wlua_prefix = "wlua54", + dll_name = "lua54.dll", + }, + ["5.3.6"] = { + archive_name = "lua-5.3.6_Win64_bin.zip", + relative_path = "5.3.6/Tools%20Executables/lua-5.3.6_Win64_bin.zip", + executable_prefix = "lua53", + wlua_prefix = "wlua53", + dll_name = "lua53.dll", + }, + ["5.2.4"] = { + archive_name = "lua-5.2.4_Win64_bin.zip", + relative_path = "5.2.4/Tools%20Executables/lua-5.2.4_Win64_bin.zip", + executable_prefix = "lua52", + wlua_prefix = "wlua52", + dll_name = "lua52.dll", + }, +} local function parse_version_line(line) return string.match(line, "([^,]+),([^,]+)") @@ -42,6 +72,31 @@ function lua_utils.get_version_info(lua_version) return nil, nil end +function lua_utils.use_windows_luabinaries() + local flag = os.getenv("VFOX_LUA_WINDOWS_LUABINARIES") + return flag ~= nil and flag ~= "" and flag ~= "0" and flag ~= "false" +end + +function lua_utils.get_windows_luabinaries_package(lua_version) + if RUNTIME.osType ~= "windows" then + return nil + end + + local package = windows_luabinaries_packages[lua_version] + if package == nil then + return nil + end + + return { + archive_name = package.archive_name, + executable_prefix = package.executable_prefix, + wlua_prefix = package.wlua_prefix, + dll_name = package.dll_name, + url = "https://sourceforge.net/projects/luabinaries/files/" .. + package.relative_path .. "/download?use_mirror=autoselect", + } +end + function lua_utils.is_dir(path) local status = os.execute("[ -d " .. path .. " ]") return status == 0 From ea45e0e3ed16be839f5b519fd0fcb02677c654ed Mon Sep 17 00:00:00 2001 From: yeshan333 Date: Sat, 23 May 2026 14:51:18 +0800 Subject: [PATCH 02/15] docs: document windows luabinaries option --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index 10ca310..42ac33a 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,10 @@ Lua [vfox](https://github.com/version-fox) plugin. Use the vfox to manage multip - GCC compiler - Make +Optional on Windows: +- Set `VFOX_LUA_WINDOWS_LUABINARIES=1` to install a prebuilt LuaBinaries package instead of compiling from source +- Supported LuaBinaries versions: `5.5.0`, `5.4.8`, `5.3.6`, `5.2.4` + ## Usage ### Install with vfox @@ -38,6 +42,15 @@ vfox install lua@5.4.7 vfox use -g lua@5.4.7 ``` +On Windows, you can opt into prebuilt LuaBinaries packages instead of the default MSYS2 source build: + +```powershell +$env:VFOX_LUA_WINDOWS_LUABINARIES=1 +vfox install lua@5.5.0 +vfox use -g lua@5.5.0 +lua -v +``` + ### Install with mise The vfox-lua plugin can also be used through [mise](https://mise.jdx.dev/), which supports vfox plugins. @@ -92,6 +105,8 @@ luarocks install luacheck 3. On Windows, use `PowerShell` to install Lua. +4. `VFOX_LUA_WINDOWS_LUABINARIES=1` is a Windows-only opt-in. If the requested version is not published by LuaBinaries, the install will fail and you should use the default source-build flow instead. + ## Known Issues - Lua versions 5.0 and earlier cannot be installed on Linux. From 6760d8dc74c648979b94af71351fba72dece198a Mon Sep 17 00:00:00 2001 From: yeshan333 Date: Sat, 23 May 2026 14:51:22 +0800 Subject: [PATCH 03/15] test: cover windows luabinaries installs in ci --- .github/workflows/e2e_test.yaml | 50 +++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/.github/workflows/e2e_test.yaml b/.github/workflows/e2e_test.yaml index 26aa7c9..784556d 100644 --- a/.github/workflows/e2e_test.yaml +++ b/.github/workflows/e2e_test.yaml @@ -269,3 +269,53 @@ jobs: $HOME/.local/bin/mise exec -- luarocks --version $HOME/.local/bin/mise exec -- luarocks install luacheck $HOME/.local/bin/mise exec -- luarocks list | grep luacheck + + windows_luabinaries_tests: + strategy: + matrix: + lua_version: [5.5.0, 5.4.8, 5.3.6] + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-go@v5 + with: + go-version: '^1.24.2' + + - name: build & install vfox + shell: pwsh + run: | + git clone https://github.com/version-fox/vfox.git + cd vfox + go build -o vfox.exe + echo "$pwd" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + ./vfox.exe -version + + - name: add vfox-lua plugin + shell: pwsh + run: | + vfox add --source https://github.com/${{ github.repository_owner }}/vfox-lua/archive/$env:GITHUB_REF.zip lua + + - name: install Lua from LuaBinaries by vfox-lua plugin + shell: pwsh + env: + LUA_VERSION: "${{ matrix.lua_version }}" + VFOX_LUA_WINDOWS_LUABINARIES: "1" + run: | + vfox install "lua@$env:LUA_VERSION" + Invoke-Expression "$(vfox activate pwsh)" + vfox use -g "lua@$env:LUA_VERSION" + Invoke-Expression "$(vfox activate pwsh)" + echo "===============PATH===============" + echo $env:PATH + echo "===============PATH===============" + $luaVersion = lua.exe -v + $luacVersion = luac.exe -v + if ($luaVersion -notmatch "Lua $env:LUA_VERSION") { + Write-Output "lua@$env:LUA_VERSION installed from LuaBinaries failed!" + exit 1 + } + if ($luacVersion -notmatch "Lua $env:LUA_VERSION") { + Write-Output "luac@$env:LUA_VERSION installed from LuaBinaries failed!" + exit 1 + } From 0d4df07f7b24913e5a4b32a452743d2bd997f59d Mon Sep 17 00:00:00 2001 From: yeshan333 Date: Sat, 23 May 2026 14:53:52 +0800 Subject: [PATCH 04/15] fix: escape windows luabinaries install command --- hooks/post_install.lua | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/hooks/post_install.lua b/hooks/post_install.lua index 5c37f47..528242e 100644 --- a/hooks/post_install.lua +++ b/hooks/post_install.lua @@ -31,21 +31,7 @@ local function InstallWindowsLuaBinaries(path, lua_version) end local cmd = string.format([=[ -powershell -Command " -$ErrorActionPreference = 'Stop'; -$installDir = '%s'; -$binDir = Join-Path $installDir 'bin'; -New-Item -ItemType Directory -Force -Path $binDir | Out-Null; -$luaExe = Join-Path $installDir '%s.exe'; -$luacExe = Join-Path $installDir 'luac%s.exe'; -$wluaExe = Join-Path $installDir '%s.exe'; -$luaDll = Join-Path $installDir '%s'; -if (-not (Test-Path $luaExe)) { throw 'LuaBinaries executable package layout is unexpected.' } -Copy-Item -Path $luaExe -Destination (Join-Path $binDir 'lua.exe') -Force; -if (Test-Path $luacExe) { Copy-Item -Path $luacExe -Destination (Join-Path $binDir 'luac.exe') -Force; } -if (Test-Path $wluaExe) { Copy-Item -Path $wluaExe -Destination (Join-Path $binDir 'wlua.exe') -Force; } -if (Test-Path $luaDll) { Copy-Item -Path $luaDll -Destination (Join-Path $binDir '%s') -Force; } -" +powershell -NoProfile -Command "& { $ErrorActionPreference = 'Stop'; $installDir = '%s'; $binDir = Join-Path $installDir 'bin'; New-Item -ItemType Directory -Force -Path $binDir | Out-Null; $luaExe = Join-Path $installDir '%s.exe'; $luacExe = Join-Path $installDir 'luac%s.exe'; $wluaExe = Join-Path $installDir '%s.exe'; $luaDll = Join-Path $installDir '%s'; if (-not (Test-Path $luaExe)) { throw 'LuaBinaries executable package layout is unexpected.' }; Copy-Item -Path $luaExe -Destination (Join-Path $binDir 'lua.exe') -Force; if (Test-Path $luacExe) { Copy-Item -Path $luacExe -Destination (Join-Path $binDir 'luac.exe') -Force }; if (Test-Path $wluaExe) { Copy-Item -Path $wluaExe -Destination (Join-Path $binDir 'wlua.exe') -Force }; if (Test-Path $luaDll) { Copy-Item -Path $luaDll -Destination (Join-Path $binDir '%s') -Force } }" ]=], path, package.executable_prefix, From dc632b5d6e3a485b05aad83bbbdd6841149493ae Mon Sep 17 00:00:00 2001 From: yeshan333 Date: Sat, 23 May 2026 14:56:09 +0800 Subject: [PATCH 05/15] fix: run luabinaries setup from a ps1 script --- hooks/post_install.lua | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/hooks/post_install.lua b/hooks/post_install.lua index 528242e..d609e1b 100644 --- a/hooks/post_install.lua +++ b/hooks/post_install.lua @@ -30,8 +30,21 @@ local function InstallWindowsLuaBinaries(path, lua_version) error("LuaBinaries package metadata missing for version " .. lua_version) end - local cmd = string.format([=[ -powershell -NoProfile -Command "& { $ErrorActionPreference = 'Stop'; $installDir = '%s'; $binDir = Join-Path $installDir 'bin'; New-Item -ItemType Directory -Force -Path $binDir | Out-Null; $luaExe = Join-Path $installDir '%s.exe'; $luacExe = Join-Path $installDir 'luac%s.exe'; $wluaExe = Join-Path $installDir '%s.exe'; $luaDll = Join-Path $installDir '%s'; if (-not (Test-Path $luaExe)) { throw 'LuaBinaries executable package layout is unexpected.' }; Copy-Item -Path $luaExe -Destination (Join-Path $binDir 'lua.exe') -Force; if (Test-Path $luacExe) { Copy-Item -Path $luacExe -Destination (Join-Path $binDir 'luac.exe') -Force }; if (Test-Path $wluaExe) { Copy-Item -Path $wluaExe -Destination (Join-Path $binDir 'wlua.exe') -Force }; if (Test-Path $luaDll) { Copy-Item -Path $luaDll -Destination (Join-Path $binDir '%s') -Force } }" + local script_path = path .. "\\install_luabinaries.ps1" + local script_content = string.format([=[ +$ErrorActionPreference = 'Stop' +$installDir = '%s' +$binDir = Join-Path $installDir 'bin' +New-Item -ItemType Directory -Force -Path $binDir | Out-Null +$luaExe = Join-Path $installDir '%s.exe' +$luacExe = Join-Path $installDir 'luac%s.exe' +$wluaExe = Join-Path $installDir '%s.exe' +$luaDll = Join-Path $installDir '%s' +if (-not (Test-Path $luaExe)) { throw 'LuaBinaries executable package layout is unexpected.' } +Copy-Item -Path $luaExe -Destination (Join-Path $binDir 'lua.exe') -Force +if (Test-Path $luacExe) { Copy-Item -Path $luacExe -Destination (Join-Path $binDir 'luac.exe') -Force } +if (Test-Path $wluaExe) { Copy-Item -Path $wluaExe -Destination (Join-Path $binDir 'wlua.exe') -Force } +if (Test-Path $luaDll) { Copy-Item -Path $luaDll -Destination (Join-Path $binDir '%s') -Force } ]=], path, package.executable_prefix, @@ -41,7 +54,19 @@ powershell -NoProfile -Command "& { $ErrorActionPreference = 'Stop'; $installDir package.dll_name ) + local script_file = io.open(script_path, "w") + if script_file == nil then + error("failed to create temporary LuaBinaries install script") + end + script_file:write(script_content) + script_file:close() + + local cmd = string.format( + "powershell -NoProfile -ExecutionPolicy Bypass -File \"%s\"", + script_path + ) local status = os.execute(cmd) + os.remove(script_path) if status ~= 0 then error("failed to prepare LuaBinaries files for Windows") end From fbe0ad3c56bf388d5b2a071706805bde5c3b31e9 Mon Sep 17 00:00:00 2001 From: yeshan333 Date: Sat, 23 May 2026 14:58:24 +0800 Subject: [PATCH 06/15] fix: expose luabinaries executables in sdk paths --- hooks/env_keys.lua | 10 ++++++++++ hooks/post_install.lua | 5 +++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/hooks/env_keys.lua b/hooks/env_keys.lua index 5903be1..a0fa9c7 100644 --- a/hooks/env_keys.lua +++ b/hooks/env_keys.lua @@ -16,6 +16,16 @@ function PLUGIN:EnvKeys(ctx) }, } + local rootLua = installDir .. "/lua.exe" + local rootLuaFile = io.open(rootLua, "r") + if rootLuaFile ~= nil then + rootLuaFile:close() + table.insert(envs, 1, { + key = "PATH", + value = installDir, + }) + end + local luarocksBin = installDir .. "/luarocks/bin" local f = io.open(luarocksBin, "r") if f ~= nil then diff --git a/hooks/post_install.lua b/hooks/post_install.lua index d609e1b..2cead45 100644 --- a/hooks/post_install.lua +++ b/hooks/post_install.lua @@ -41,9 +41,10 @@ $luacExe = Join-Path $installDir 'luac%s.exe' $wluaExe = Join-Path $installDir '%s.exe' $luaDll = Join-Path $installDir '%s' if (-not (Test-Path $luaExe)) { throw 'LuaBinaries executable package layout is unexpected.' } +Copy-Item -Path $luaExe -Destination (Join-Path $installDir 'lua.exe') -Force Copy-Item -Path $luaExe -Destination (Join-Path $binDir 'lua.exe') -Force -if (Test-Path $luacExe) { Copy-Item -Path $luacExe -Destination (Join-Path $binDir 'luac.exe') -Force } -if (Test-Path $wluaExe) { Copy-Item -Path $wluaExe -Destination (Join-Path $binDir 'wlua.exe') -Force } +if (Test-Path $luacExe) { Copy-Item -Path $luacExe -Destination (Join-Path $installDir 'luac.exe') -Force; Copy-Item -Path $luacExe -Destination (Join-Path $binDir 'luac.exe') -Force } +if (Test-Path $wluaExe) { Copy-Item -Path $wluaExe -Destination (Join-Path $installDir 'wlua.exe') -Force; Copy-Item -Path $wluaExe -Destination (Join-Path $binDir 'wlua.exe') -Force } if (Test-Path $luaDll) { Copy-Item -Path $luaDll -Destination (Join-Path $binDir '%s') -Force } ]=], path, From 2cc76240763be7b7cb07a973869dd9064faa5d83 Mon Sep 17 00:00:00 2001 From: yeshan333 Date: Sat, 23 May 2026 15:00:03 +0800 Subject: [PATCH 07/15] fix: pass ps1 path to powershell without extra quotes --- hooks/post_install.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hooks/post_install.lua b/hooks/post_install.lua index 2cead45..35ea4d6 100644 --- a/hooks/post_install.lua +++ b/hooks/post_install.lua @@ -63,7 +63,7 @@ if (Test-Path $luaDll) { Copy-Item -Path $luaDll -Destination (Join-Path $binDir script_file:close() local cmd = string.format( - "powershell -NoProfile -ExecutionPolicy Bypass -File \"%s\"", + "powershell -NoProfile -ExecutionPolicy Bypass -File %s", script_path ) local status = os.execute(cmd) From 5147147b1c8b0d43f250f4be6f05934ab68ba80e Mon Sep 17 00:00:00 2001 From: yeshan333 Date: Sat, 23 May 2026 15:34:46 +0800 Subject: [PATCH 08/15] fix: address luabinaries review feedback --- .github/workflows/e2e_test.yaml | 4 ++-- hooks/post_install.lua | 22 ++++++++++--------- hooks/pre_install.lua | 12 ++++++----- lib/utils.lua | 38 +++++++++++++++++++++++++++------ 4 files changed, 52 insertions(+), 24 deletions(-) diff --git a/.github/workflows/e2e_test.yaml b/.github/workflows/e2e_test.yaml index 784556d..7513913 100644 --- a/.github/workflows/e2e_test.yaml +++ b/.github/workflows/e2e_test.yaml @@ -60,7 +60,7 @@ jobs: - name: add vfox-lua plugin if: runner.os == 'Windows' run: | - vfox add --source https://github.com/${{ github.repository_owner }}/vfox-lua/archive/$env:GITHUB_REF.zip lua + vfox add --source "https://github.com/${{ github.repository_owner }}/vfox-lua/archive/${env:GITHUB_REF}.zip" lua - name: add vfox-lua plugin if: runner.os != 'Windows' @@ -294,7 +294,7 @@ jobs: - name: add vfox-lua plugin shell: pwsh run: | - vfox add --source https://github.com/${{ github.repository_owner }}/vfox-lua/archive/$env:GITHUB_REF.zip lua + vfox add --source "https://github.com/${{ github.repository_owner }}/vfox-lua/archive/${env:GITHUB_REF}.zip" lua - name: install Lua from LuaBinaries by vfox-lua plugin shell: pwsh diff --git a/hooks/post_install.lua b/hooks/post_install.lua index 35ea4d6..e8ab64f 100644 --- a/hooks/post_install.lua +++ b/hooks/post_install.lua @@ -25,8 +25,8 @@ Make sure make.exe and gcc.exe are in the System $env:PATH in PowerShell. end local function InstallWindowsLuaBinaries(path, lua_version) - local package = Utils.get_windows_luabinaries_package(lua_version) - if package == nil then + local pkg_meta = Utils.get_windows_luabinaries_package(lua_version) + if pkg_meta == nil then error("LuaBinaries package metadata missing for version " .. lua_version) end @@ -48,11 +48,11 @@ if (Test-Path $wluaExe) { Copy-Item -Path $wluaExe -Destination (Join-Path $inst if (Test-Path $luaDll) { Copy-Item -Path $luaDll -Destination (Join-Path $binDir '%s') -Force } ]=], path, - package.executable_prefix, - string.sub(package.executable_prefix, 4), - package.wlua_prefix, - package.dll_name, - package.dll_name + pkg_meta.executable_prefix, + string.sub(pkg_meta.executable_prefix, 4), + pkg_meta.wlua_prefix, + pkg_meta.dll_name, + pkg_meta.dll_name ) local script_file = io.open(script_path, "w") @@ -62,13 +62,15 @@ if (Test-Path $luaDll) { Copy-Item -Path $luaDll -Destination (Join-Path $binDir script_file:write(script_content) script_file:close() + local powershell_script_path = string.gsub(script_path, "\\", "/") + powershell_script_path = string.gsub(powershell_script_path, "'", "''") local cmd = string.format( - "powershell -NoProfile -ExecutionPolicy Bypass -File %s", - script_path + "powershell -NoProfile -ExecutionPolicy Bypass -Command \"& '%s'\"", + powershell_script_path ) local status = os.execute(cmd) os.remove(script_path) - if status ~= 0 then + if not Utils.is_success_status(status) then error("failed to prepare LuaBinaries files for Windows") end end diff --git a/hooks/pre_install.lua b/hooks/pre_install.lua index 8b4b345..8ebb1df 100644 --- a/hooks/pre_install.lua +++ b/hooks/pre_install.lua @@ -14,16 +14,18 @@ function PLUGIN:PreInstall(ctx) local download_url if RUNTIME.osType == "windows" and Utils.use_windows_luabinaries() then - local package = Utils.get_windows_luabinaries_package(lua_version) - if package == nil then + local pkg_meta = Utils.get_windows_luabinaries_package(lua_version) + if pkg_meta == nil then error("LuaBinaries does not provide a Windows binary for version " .. - lua_version .. ". Disable VFOX_LUA_WINDOWS_LUABINARIES or choose one of: 5.5.0, 5.4.8, 5.3.6, 5.2.4.") + lua_version .. ". Disable VFOX_LUA_WINDOWS_LUABINARIES or choose one of: " .. + Utils.get_windows_luabinaries_versions_text() .. ".") end - print("lua download url: " .. package.url) + print("lua download url: " .. pkg_meta.url) return { version = lua_version, - url = package.url, + url = pkg_meta.url, + sha256 = pkg_meta.sha256, } end diff --git a/lib/utils.lua b/lib/utils.lua index 294f266..56cbb3f 100644 --- a/lib/utils.lua +++ b/lib/utils.lua @@ -8,6 +8,7 @@ local windows_luabinaries_packages = { executable_prefix = "lua55", wlua_prefix = "wlua55", dll_name = "lua55.dll", + sha256 = "7825cb261d0dc61cb0e1511d451ceaf10bf72d2bba1855bfd3350add190e0024", }, ["5.4.8"] = { archive_name = "lua-5.4.8_Win64_bin.zip", @@ -15,6 +16,7 @@ local windows_luabinaries_packages = { executable_prefix = "lua54", wlua_prefix = "wlua54", dll_name = "lua54.dll", + sha256 = "9c5d151bfe2b62bd685d88bd1963c17dd5ea2fed37defcda7c02ee6e226bcc39", }, ["5.3.6"] = { archive_name = "lua-5.3.6_Win64_bin.zip", @@ -22,6 +24,7 @@ local windows_luabinaries_packages = { executable_prefix = "lua53", wlua_prefix = "wlua53", dll_name = "lua53.dll", + sha256 = "5150a30db5b62956d1bca4c2f3e5d1e08c00e398d8c902f0572b09f014012287", }, ["5.2.4"] = { archive_name = "lua-5.2.4_Win64_bin.zip", @@ -29,6 +32,7 @@ local windows_luabinaries_packages = { executable_prefix = "lua52", wlua_prefix = "wlua52", dll_name = "lua52.dll", + sha256 = "6cc8153640b5c1fc4632f18dadaa8696c5b7aef85e885245280d7e31011549d9", }, } @@ -77,26 +81,46 @@ function lua_utils.use_windows_luabinaries() return flag ~= nil and flag ~= "" and flag ~= "0" and flag ~= "false" end +function lua_utils.get_windows_luabinaries_versions() + local versions = {} + for version, _ in pairs(windows_luabinaries_packages) do + table.insert(versions, version) + end + table.sort(versions, function(a, b) + return a > b + end) + return versions +end + +function lua_utils.get_windows_luabinaries_versions_text() + return table.concat(lua_utils.get_windows_luabinaries_versions(), ", ") +end + function lua_utils.get_windows_luabinaries_package(lua_version) if RUNTIME.osType ~= "windows" then return nil end - local package = windows_luabinaries_packages[lua_version] - if package == nil then + local pkg_meta = windows_luabinaries_packages[lua_version] + if pkg_meta == nil then return nil end return { - archive_name = package.archive_name, - executable_prefix = package.executable_prefix, - wlua_prefix = package.wlua_prefix, - dll_name = package.dll_name, + archive_name = pkg_meta.archive_name, + executable_prefix = pkg_meta.executable_prefix, + wlua_prefix = pkg_meta.wlua_prefix, + dll_name = pkg_meta.dll_name, + sha256 = pkg_meta.sha256, url = "https://sourceforge.net/projects/luabinaries/files/" .. - package.relative_path .. "/download?use_mirror=autoselect", + pkg_meta.relative_path .. "/download?use_mirror=autoselect", } end +function lua_utils.is_success_status(status) + return status == true or status == 0 +end + function lua_utils.is_dir(path) local status = os.execute("[ -d " .. path .. " ]") return status == 0 From 82650aadc158a81206df88d60200761da7a363d9 Mon Sep 17 00:00:00 2001 From: yeshan333 Date: Sat, 23 May 2026 15:40:56 +0800 Subject: [PATCH 09/15] fix: skip unstable luabinaries checksum verification --- hooks/pre_install.lua | 1 - lib/utils.lua | 5 ----- 2 files changed, 6 deletions(-) diff --git a/hooks/pre_install.lua b/hooks/pre_install.lua index 8ebb1df..dd2a371 100644 --- a/hooks/pre_install.lua +++ b/hooks/pre_install.lua @@ -25,7 +25,6 @@ function PLUGIN:PreInstall(ctx) return { version = lua_version, url = pkg_meta.url, - sha256 = pkg_meta.sha256, } end diff --git a/lib/utils.lua b/lib/utils.lua index 56cbb3f..88356d5 100644 --- a/lib/utils.lua +++ b/lib/utils.lua @@ -8,7 +8,6 @@ local windows_luabinaries_packages = { executable_prefix = "lua55", wlua_prefix = "wlua55", dll_name = "lua55.dll", - sha256 = "7825cb261d0dc61cb0e1511d451ceaf10bf72d2bba1855bfd3350add190e0024", }, ["5.4.8"] = { archive_name = "lua-5.4.8_Win64_bin.zip", @@ -16,7 +15,6 @@ local windows_luabinaries_packages = { executable_prefix = "lua54", wlua_prefix = "wlua54", dll_name = "lua54.dll", - sha256 = "9c5d151bfe2b62bd685d88bd1963c17dd5ea2fed37defcda7c02ee6e226bcc39", }, ["5.3.6"] = { archive_name = "lua-5.3.6_Win64_bin.zip", @@ -24,7 +22,6 @@ local windows_luabinaries_packages = { executable_prefix = "lua53", wlua_prefix = "wlua53", dll_name = "lua53.dll", - sha256 = "5150a30db5b62956d1bca4c2f3e5d1e08c00e398d8c902f0572b09f014012287", }, ["5.2.4"] = { archive_name = "lua-5.2.4_Win64_bin.zip", @@ -32,7 +29,6 @@ local windows_luabinaries_packages = { executable_prefix = "lua52", wlua_prefix = "wlua52", dll_name = "lua52.dll", - sha256 = "6cc8153640b5c1fc4632f18dadaa8696c5b7aef85e885245280d7e31011549d9", }, } @@ -111,7 +107,6 @@ function lua_utils.get_windows_luabinaries_package(lua_version) executable_prefix = pkg_meta.executable_prefix, wlua_prefix = pkg_meta.wlua_prefix, dll_name = pkg_meta.dll_name, - sha256 = pkg_meta.sha256, url = "https://sourceforge.net/projects/luabinaries/files/" .. pkg_meta.relative_path .. "/download?use_mirror=autoselect", } From 90e12f2b49d04661ee538394846dfdfab6caa36b Mon Sep 17 00:00:00 2001 From: yeshan333 Date: Sat, 23 May 2026 15:46:17 +0800 Subject: [PATCH 10/15] fix: use version install dir for env keys --- hooks/env_keys.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hooks/env_keys.lua b/hooks/env_keys.lua index a0fa9c7..7efd53e 100644 --- a/hooks/env_keys.lua +++ b/hooks/env_keys.lua @@ -5,7 +5,7 @@ function PLUGIN:EnvKeys(ctx) local sdkInfo = ctx.sdkInfo["lua"] local version = sdkInfo.version - local installDir = sdkInfo.path + local installDir = ctx.path local shortVersion = string.match(version, "^(%d+%.%d+)") From 902c9c93303b4a0bce73c204cf239e8d52becab5 Mon Sep 17 00:00:00 2001 From: yeshan333 Date: Sat, 23 May 2026 15:50:09 +0800 Subject: [PATCH 11/15] fix: always expose luabinaries root on windows --- hooks/env_keys.lua | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/hooks/env_keys.lua b/hooks/env_keys.lua index 7efd53e..bccf22b 100644 --- a/hooks/env_keys.lua +++ b/hooks/env_keys.lua @@ -1,3 +1,5 @@ +local Utils = require("utils") + --- Each SDK may have different environment variable configurations. --- This allows plugins to define custom environment variables (including PATH settings) --- @param ctx table Context information @@ -16,10 +18,7 @@ function PLUGIN:EnvKeys(ctx) }, } - local rootLua = installDir .. "/lua.exe" - local rootLuaFile = io.open(rootLua, "r") - if rootLuaFile ~= nil then - rootLuaFile:close() + if RUNTIME.osType == "windows" and Utils.use_windows_luabinaries() then table.insert(envs, 1, { key = "PATH", value = installDir, From 4a29fc4e309efebe2d9bdd2915e8f1aaadc7b339 Mon Sep 17 00:00:00 2001 From: yeshan333 Date: Sat, 23 May 2026 16:49:41 +0800 Subject: [PATCH 12/15] fix: copy LuaBinaries files via lua io instead of powershell The previous PowerShell-based copy was being echoed to stdout instead of executed in CI, leaving lua.exe missing from the install dir. Use binary io.open read/write so the copies are independent of any shell quoting or invocation rules. Co-Authored-By: Claude Opus 4.7 --- hooks/post_install.lua | 101 ++++++++++++++++++++++++----------------- 1 file changed, 60 insertions(+), 41 deletions(-) diff --git a/hooks/post_install.lua b/hooks/post_install.lua index e8ab64f..da711fc 100644 --- a/hooks/post_install.lua +++ b/hooks/post_install.lua @@ -24,54 +24,73 @@ Make sure make.exe and gcc.exe are in the System $env:PATH in PowerShell. end end +local function copy_file(src, dst) + local input = io.open(src, "rb") + if input == nil then + return false + end + local content = input:read("*a") + input:close() + + local output = io.open(dst, "wb") + if output == nil then + return false + end + output:write(content) + output:close() + return true +end + +local function file_exists(path) + local f = io.open(path, "rb") + if f == nil then + return false + end + f:close() + return true +end + local function InstallWindowsLuaBinaries(path, lua_version) local pkg_meta = Utils.get_windows_luabinaries_package(lua_version) if pkg_meta == nil then error("LuaBinaries package metadata missing for version " .. lua_version) end - local script_path = path .. "\\install_luabinaries.ps1" - local script_content = string.format([=[ -$ErrorActionPreference = 'Stop' -$installDir = '%s' -$binDir = Join-Path $installDir 'bin' -New-Item -ItemType Directory -Force -Path $binDir | Out-Null -$luaExe = Join-Path $installDir '%s.exe' -$luacExe = Join-Path $installDir 'luac%s.exe' -$wluaExe = Join-Path $installDir '%s.exe' -$luaDll = Join-Path $installDir '%s' -if (-not (Test-Path $luaExe)) { throw 'LuaBinaries executable package layout is unexpected.' } -Copy-Item -Path $luaExe -Destination (Join-Path $installDir 'lua.exe') -Force -Copy-Item -Path $luaExe -Destination (Join-Path $binDir 'lua.exe') -Force -if (Test-Path $luacExe) { Copy-Item -Path $luacExe -Destination (Join-Path $installDir 'luac.exe') -Force; Copy-Item -Path $luacExe -Destination (Join-Path $binDir 'luac.exe') -Force } -if (Test-Path $wluaExe) { Copy-Item -Path $wluaExe -Destination (Join-Path $installDir 'wlua.exe') -Force; Copy-Item -Path $wluaExe -Destination (Join-Path $binDir 'wlua.exe') -Force } -if (Test-Path $luaDll) { Copy-Item -Path $luaDll -Destination (Join-Path $binDir '%s') -Force } -]=], - path, - pkg_meta.executable_prefix, - string.sub(pkg_meta.executable_prefix, 4), - pkg_meta.wlua_prefix, - pkg_meta.dll_name, - pkg_meta.dll_name - ) - - local script_file = io.open(script_path, "w") - if script_file == nil then - error("failed to create temporary LuaBinaries install script") + local bin_dir = path .. "\\bin" + os.execute(string.format('if not exist "%s" mkdir "%s"', bin_dir, bin_dir)) + + local lua_exe = path .. "\\" .. pkg_meta.executable_prefix .. ".exe" + if not file_exists(lua_exe) then + error("LuaBinaries executable not found at " .. lua_exe) + end + + local copies = { + { src = lua_exe, dst = path .. "\\lua.exe" }, + { src = lua_exe, dst = bin_dir .. "\\lua.exe" }, + } + + local luac_prefix = "luac" .. string.sub(pkg_meta.executable_prefix, 4) + local luac_exe = path .. "\\" .. luac_prefix .. ".exe" + if file_exists(luac_exe) then + table.insert(copies, { src = luac_exe, dst = path .. "\\luac.exe" }) + table.insert(copies, { src = luac_exe, dst = bin_dir .. "\\luac.exe" }) + end + + local wlua_exe = path .. "\\" .. pkg_meta.wlua_prefix .. ".exe" + if file_exists(wlua_exe) then + table.insert(copies, { src = wlua_exe, dst = path .. "\\wlua.exe" }) + table.insert(copies, { src = wlua_exe, dst = bin_dir .. "\\wlua.exe" }) end - script_file:write(script_content) - script_file:close() - - local powershell_script_path = string.gsub(script_path, "\\", "/") - powershell_script_path = string.gsub(powershell_script_path, "'", "''") - local cmd = string.format( - "powershell -NoProfile -ExecutionPolicy Bypass -Command \"& '%s'\"", - powershell_script_path - ) - local status = os.execute(cmd) - os.remove(script_path) - if not Utils.is_success_status(status) then - error("failed to prepare LuaBinaries files for Windows") + + local lua_dll = path .. "\\" .. pkg_meta.dll_name + if file_exists(lua_dll) then + table.insert(copies, { src = lua_dll, dst = bin_dir .. "\\" .. pkg_meta.dll_name }) + end + + for _, item in ipairs(copies) do + if not copy_file(item.src, item.dst) then + error("failed to copy " .. item.src .. " to " .. item.dst) + end end end From 79b6f0913a1f98c8780d1ee15768b90a66b3a812 Mon Sep 17 00:00:00 2001 From: yeshan333 Date: Sat, 23 May 2026 16:55:26 +0800 Subject: [PATCH 13/15] fix: drop bin/ directory creation for luabinaries install The mkdir for an extra bin/ subdirectory was failing on the Windows runner ("The filename, directory name, or volume label syntax is incorrect"). Since env_keys.lua already prepends the install root to PATH, the canonical-name aliases (lua.exe, luac.exe, wlua.exe) only need to live in the install root next to the original lua54.exe etc. This removes the only os.execute call from the LuaBinaries path. Co-Authored-By: Claude Opus 4.7 --- hooks/post_install.lua | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/hooks/post_install.lua b/hooks/post_install.lua index da711fc..4b3b7c8 100644 --- a/hooks/post_install.lua +++ b/hooks/post_install.lua @@ -56,9 +56,10 @@ local function InstallWindowsLuaBinaries(path, lua_version) error("LuaBinaries package metadata missing for version " .. lua_version) end - local bin_dir = path .. "\\bin" - os.execute(string.format('if not exist "%s" mkdir "%s"', bin_dir, bin_dir)) - + -- LuaBinaries archives expand executables (lua54.exe, luac54.exe, ...) into the install + -- root next to the DLL. We add aliases (lua.exe, luac.exe, wlua.exe) in the same folder + -- so users can invoke them by their canonical names. env_keys.lua already prepends the + -- install root to PATH, so a separate bin/ directory isn't needed. local lua_exe = path .. "\\" .. pkg_meta.executable_prefix .. ".exe" if not file_exists(lua_exe) then error("LuaBinaries executable not found at " .. lua_exe) @@ -66,25 +67,17 @@ local function InstallWindowsLuaBinaries(path, lua_version) local copies = { { src = lua_exe, dst = path .. "\\lua.exe" }, - { src = lua_exe, dst = bin_dir .. "\\lua.exe" }, } local luac_prefix = "luac" .. string.sub(pkg_meta.executable_prefix, 4) local luac_exe = path .. "\\" .. luac_prefix .. ".exe" if file_exists(luac_exe) then table.insert(copies, { src = luac_exe, dst = path .. "\\luac.exe" }) - table.insert(copies, { src = luac_exe, dst = bin_dir .. "\\luac.exe" }) end local wlua_exe = path .. "\\" .. pkg_meta.wlua_prefix .. ".exe" if file_exists(wlua_exe) then table.insert(copies, { src = wlua_exe, dst = path .. "\\wlua.exe" }) - table.insert(copies, { src = wlua_exe, dst = bin_dir .. "\\wlua.exe" }) - end - - local lua_dll = path .. "\\" .. pkg_meta.dll_name - if file_exists(lua_dll) then - table.insert(copies, { src = lua_dll, dst = bin_dir .. "\\" .. pkg_meta.dll_name }) end for _, item in ipairs(copies) do From 8e1b3bb1d0370d843045f38b1d8eb2143e0ba22d Mon Sep 17 00:00:00 2001 From: yeshan333 Date: Sun, 24 May 2026 13:40:38 +0800 Subject: [PATCH 14/15] docs: explain why luabinaries install skips checksum verification Why: PR #13 reviewer flagged the supply-chain risk of installing prebuilt LuaBinaries archives without integrity checking. The skip is deliberate (SourceForge mirror-redirect breaks the hash), but that rationale wasn't visible in the code or to end users. Co-Authored-By: Claude Opus 4.7 --- README.md | 2 +- hooks/pre_install.lua | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 42ac33a..420cdc1 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,7 @@ luarocks install luacheck 3. On Windows, use `PowerShell` to install Lua. -4. `VFOX_LUA_WINDOWS_LUABINARIES=1` is a Windows-only opt-in. If the requested version is not published by LuaBinaries, the install will fail and you should use the default source-build flow instead. +4. `VFOX_LUA_WINDOWS_LUABINARIES=1` is a Windows-only opt-in. If the requested version is not published by LuaBinaries, the install will fail and you should use the default source-build flow instead. The archive is downloaded from SourceForge's mirror autoselect over HTTPS; checksum verification is currently skipped because the redirected mirrors don't yield a stable hash. Prefer the default source-build flow if you require an integrity check. ## Known Issues diff --git a/hooks/pre_install.lua b/hooks/pre_install.lua index dd2a371..82ca34f 100644 --- a/hooks/pre_install.lua +++ b/hooks/pre_install.lua @@ -22,6 +22,11 @@ function PLUGIN:PreInstall(ctx) end print("lua download url: " .. pkg_meta.url) + -- LuaBinaries is distributed via SourceForge's mirror autoselect, which redirects to + -- a rotating set of mirrors. vfox computed checksums sometimes hashed the redirect HTML + -- instead of the archive bytes, so the integrity check was unreliable. Until upstream + -- publishes signed checksums we can pin, the opt-in install relies on HTTPS only — + -- documented as a trade-off in the README. return { version = lua_version, url = pkg_meta.url, From 100cf3218e12fb6b5f55c95d3e8600b4420ffd75 Mon Sep 17 00:00:00 2001 From: yeshan333 Date: Sun, 24 May 2026 14:13:21 +0800 Subject: [PATCH 15/15] fix: detect luabinaries layout by probing lua.exe instead of env flag Why: Codex review on PR #13 flagged that EnvKeys was gating the extra PATH entry on VFOX_LUA_WINDOWS_LUABINARIES. That flag is an install-time opt-in and isn't persisted per SDK, so any later `vfox use` in a shell without the flag would leave the LuaBinaries install unusable. Probing for the root lua.exe is stable across activation shells and cleanly distinguishes the LuaBinaries layout from the MSYS2 source build (which only populates bin/). Co-Authored-By: Claude Opus 4.7 --- hooks/env_keys.lua | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/hooks/env_keys.lua b/hooks/env_keys.lua index bccf22b..610ccb0 100644 --- a/hooks/env_keys.lua +++ b/hooks/env_keys.lua @@ -1,5 +1,3 @@ -local Utils = require("utils") - --- Each SDK may have different environment variable configurations. --- This allows plugins to define custom environment variables (including PATH settings) --- @param ctx table Context information @@ -18,11 +16,19 @@ function PLUGIN:EnvKeys(ctx) }, } - if RUNTIME.osType == "windows" and Utils.use_windows_luabinaries() then - table.insert(envs, 1, { - key = "PATH", - value = installDir, - }) + -- LuaBinaries installs place lua.exe in the install root next to the DLL; MSYS2 source + -- builds only populate bin/. Detect the LuaBinaries layout by probing the file so PATH + -- stays correct on `vfox use` even when VFOX_LUA_WINDOWS_LUABINARIES (an install-time + -- opt-in) is no longer exported in the activation shell. + if RUNTIME.osType == "windows" then + local rootLua = io.open(installDir .. "/lua.exe", "r") + if rootLua ~= nil then + rootLua:close() + table.insert(envs, 1, { + key = "PATH", + value = installDir, + }) + end end local luarocksBin = installDir .. "/luarocks/bin"