Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
name: Test Plugin

on:
workflow_dispatch:
pull_request:
push:
branches: [main]
Expand All @@ -23,3 +24,17 @@ jobs:
run: |
go run github.com/yuin/gopher-lua/cmd/glua@v1.1.1 tests/pre_use_test.lua
go run github.com/yuin/gopher-lua/cmd/glua@v1.1.1 tests/loongnix_test.lua
- name: Test macOS Java home normalization
run: go run github.com/yuin/gopher-lua/cmd/glua@v1.1.1 tests/post_install_test.lua
macos-layout:
runs-on: macos-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6
with:
go-version: '1.24.0'
cache: false
- name: Test actual macOS filesystem layouts
run: go run github.com/yuin/gopher-lua/cmd/glua@v1.1.1 tests/post_install_test.lua
78 changes: 53 additions & 25 deletions hooks/post_install.lua
Original file line number Diff line number Diff line change
@@ -1,32 +1,60 @@
local strings = require("vfox.strings")
local function shellQuote(value)
return "'" .. value:gsub("'", "'\\''") .. "'"
end

function PLUGIN:PostInstall(ctx)
if RUNTIME.osType ~= "darwin" then
return
end
local sdkInfo = ctx.sdkInfo['java']
local path = sdkInfo.path
local majorVersion = sdkInfo.note
local version = strings.split(sdkInfo.version, "-")[1]
local v = strings.split(version, "+")[1]
local needRemoveDir = {
['/jdk-' .. v .. '.jdk'] = true,
['/jdk-' .. majorVersion .. '.jdk'] = true,
['/jdk-' .. version .. '.jdk'] = true,
['/Contents/Home'] = true,
}
print("Checking if need to rename jdk files...")
for dir, _ in pairs(needRemoveDir) do
print("Checking path: " .. path .. dir)
if checkDir(path .. dir) then
print("Renaming jdk files: " .. path .. dir .. '/*')
if os.execute('mv ' .. path .. dir .. '/*' .. ' ' .. path) == 1 then
error('Failed to rename jdk files')
end
end
local path = ctx.sdkInfo.java.path
-- Archives may contain a flat JDK, Contents/Home, or a vendor-named
-- bundle. Identify the Java home by its executable, not its version name.
-- Move directory entries rather than copying so symlinks remain intact.
local script = [[
root=$1
if [ -x "$root/bin/java" ]; then
exit 0
fi
home=
candidate() {
if [ -d "$1" ] && [ ! -L "$1" ] && [ -x "$1/bin/java" ]; then
if [ -n "$home" ] && [ "$home" != "$1" ]; then
echo "Multiple Java homes found in $root" >&2
exit 1
fi
home=$1
fi
}
if [ ! -L "$root/Contents" ]; then
candidate "$root/Contents/Home"
fi
for bundle in "$root"/*; do
[ -d "$bundle" ] && [ ! -L "$bundle" ] || continue
candidate "$bundle"
if [ ! -L "$bundle/Contents" ]; then
candidate "$bundle/Contents/Home"
fi
done
if [ -z "$home" ]; then
echo "No Java home containing bin/java found in $root" >&2
exit 1
fi
# Check all destinations before moving anything, including hidden entries.
for entry in "$home"/* "$home"/.[!.]* "$home"/..?*; do
[ -e "$entry" ] || [ -L "$entry" ] || continue
target="$root/${entry##*/}"
if [ -e "$target" ] || [ -L "$target" ]; then
echo "Cannot normalize Java home: destination already exists: $target" >&2
exit 1
fi
done
for entry in "$home"/* "$home"/.[!.]* "$home"/..?*; do
[ -e "$entry" ] || [ -L "$entry" ] || continue
mv "$entry" "$root/" || exit 1
done
]]
print("Checking Java home layout...")
if os.execute('sh -c ' .. shellQuote(script) .. ' sh ' .. shellQuote(path)) ~= 0 then
error('Failed to normalize Java home: ' .. path)
end
end

function checkDir(path)
return os.execute('[ -d "' .. path .. '" ]') == 0
end
82 changes: 82 additions & 0 deletions tests/post_install_test.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
dofile('tests/support.lua')
PLUGIN = {}
RUNTIME = {osType = 'darwin'}
dofile('hooks/post_install.lua')

local function quote(value)
return "'" .. value:gsub("'", "'\\''") .. "'"
end
local function command(value)
assert(os.execute(value) == 0, value)
end
local function write(path, content)
local file = assert(io.open(path, 'w'))
assert(file:write(content))
assert(file:close())
end
local function read(path)
local file = io.open(path, 'r')
if not file then return nil end
local content = file:read('*a')
file:close()
return content
end
local base = os.tmpname()
os.remove(base)
base = base .. " JDK's & spaces"
command('mkdir -p ' .. quote(base))
local count = 0
local function fixture(layout)
count = count + 1
local root = base .. '/' .. count
local home = root .. layout
command('mkdir -p ' .. quote(home .. '/bin') .. ' ' .. quote(home .. '/lib'))
write(home .. '/bin/java', '#!/bin/sh\nexit 0\n')
command('chmod +x ' .. quote(home .. '/bin/java'))
write(home .. '/lib/ct.sym', 'jdk symbols')
write(home .. '/.metadata', 'hidden metadata')
command('ln -s java ' .. quote(home .. '/bin/java-link'))
return root, home
end
local function install(root)
PLUGIN:PostInstall({sdkInfo = {java = {
path = root, version = '8.0.482+10-librca', note = '8'
}}})
end
local ok, err = pcall(function()
-- The reported Liberica layout, plus existing bundle and flat layouts.
for _, layout in ipairs({
'/jdk8u482-lite.jdk', '/jdk-8.jdk/Contents/Home',
'/jdk-21.0.2.jdk/Contents/Home', '/Contents/Home', '',
'/vendor-jdk/Contents/Home',
}) do
local root = fixture(layout)
install(root)
assert(read(root .. '/bin/java'), 'JAVA_HOME/bin/java missing for ' .. layout)
assert(read(root .. '/lib/ct.sym') == 'jdk symbols', layout)
assert(read(root .. '/.metadata') == 'hidden metadata', 'hidden file lost')
command('test -L ' .. quote(root .. '/bin/java-link'))
end

-- Refuse ambiguous archives or collisions before moving anything.
local root, home = fixture('/jdk8u482-lite.jdk')
command('mkdir -p ' .. quote(root .. '/lib'))
write(root .. '/lib/keep', 'existing file')
assert(not pcall(install, root), 'must reject an existing destination')
assert(read(home .. '/bin/java') and read(root .. '/lib/keep') == 'existing file')
root, home = fixture('/one.jdk')
command('mkdir -p ' .. quote(root .. '/two.jdk/bin'))
write(root .. '/two.jdk/bin/java', '#!/bin/sh\nexit 0\n')
command('chmod +x ' .. quote(root .. '/two.jdk/bin/java'))
assert(not pcall(install, root), 'must reject multiple JAVA_HOME candidates')
assert(read(home .. '/bin/java') and read(root .. '/two.jdk/bin/java'))

-- Other platforms retain their layout.
root, home = fixture('/jdk8u482-lite.jdk')
RUNTIME.osType = 'linux'
install(root)
assert(read(home .. '/bin/java') and not read(root .. '/bin/java'))
end)
command('rm -rf ' .. quote(base))
assert(ok, err)
print('PASS: macOS JAVA_HOME layouts, metadata, links and collision checks')