Skip to content

Commit e7cfcdd

Browse files
jbadeaujbadeau
authored andcommitted
fix: Fixed vscode extension install
1 parent 51ac767 commit e7cfcdd

3 files changed

Lines changed: 168 additions & 17 deletions

File tree

lib/shell.lua

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ end
1212
-- Try to execute shell command, return success status and result
1313
function M.try_exec(fmt, ...)
1414
local args = {...}
15+
local unpack = table.unpack or unpack -- Compatibility with Lua 5.1/5.2
1516
local ok, result = pcall(function()
16-
return M.exec(fmt, table.unpack(args))
17+
return M.exec(fmt, unpack(args))
1718
end)
1819
return ok, result
1920
end

lib/vscode.lua

Lines changed: 165 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -40,28 +40,167 @@ end
4040

4141
-- VSIX installation (for VSCode recognition)
4242
function M.install_via_vsix(vsix_path)
43-
local ok, output = shell.try_exec('code --install-extension "%s"', vsix_path)
43+
local ok, output = shell.try_exec('code --install-extension "%s" 2>&1', vsix_path)
4444

45-
if not ok then
45+
-- VSCode might return non-zero exit code even on success, so check output content
46+
if output and (output:match("successfully installed") or output:match("Extension.*installed")) then
47+
logger.done("VSCode extension installed via VSIX")
48+
-- Print the success message
49+
for line in output:gmatch("[^\n]+") do
50+
if line:match("successfully installed") or line:match("Extension.*installed") then
51+
print(" " .. line)
52+
end
53+
end
54+
return true, "installed"
55+
elseif output and output:match("is already installed") then
56+
logger.info("VSCode extension already installed")
57+
return true, "already_installed"
58+
else
59+
-- If we get here, there was likely a real failure
4660
logger.fail("VSCode VSIX installation failed")
61+
if output and output ~= "" then
62+
print(" Error: " .. output)
63+
end
4764
return false, output
4865
end
66+
end
67+
68+
-- Create required VSIX manifest files
69+
function M.create_vsix_manifest(temp_dir, ext_id, ext_path)
70+
-- Read package.json to extract extension metadata
71+
local package_json_path = ext_path .. "/package.json"
72+
local package_json_content = ""
73+
local ok, result = shell.try_exec('cat "%s"', package_json_path)
74+
if ok then
75+
package_json_content = result
76+
end
77+
local package_data = {}
4978

50-
if output and output:match("is already installed") then
51-
logger.info("VSCode extension already installed")
52-
return true, "already_installed"
79+
if package_json_content then
80+
-- Simple JSON parsing for the fields we need
81+
package_data.name = package_json_content:match('"name"%s*:%s*"([^"]+)"') or ext_id
82+
package_data.displayName = package_json_content:match('"displayName"%s*:%s*"([^"]+)"') or package_data.name
83+
package_data.description = package_json_content:match('"description"%s*:%s*"([^"]+)"') or ""
84+
package_data.version = package_json_content:match('"version"%s*:%s*"([^"]+)"') or "1.0.0"
85+
package_data.publisher = package_json_content:match('"publisher"%s*:%s*"([^"]+)"') or "unknown"
86+
package_data.categories = package_json_content:match('"categories"%s*:%s*%[([^%]]+)%]') or ""
87+
package_data.keywords = package_json_content:match('"keywords"%s*:%s*%[([^%]]+)%]') or ""
88+
package_data.icon = package_json_content:match('"icon"%s*:%s*"([^"]+)"') or ""
89+
package_data.license = package_json_content:match('"license"%s*:%s*"([^"]+)"') or ""
90+
91+
-- Parse engine version
92+
local engines = package_json_content:match('"engines"%s*:%s*{([^}]+)}')
93+
if engines then
94+
package_data.engine = engines:match('"vscode"%s*:%s*"([^"]+)"') or "^1.74.0"
95+
else
96+
package_data.engine = "^1.74.0"
97+
end
5398
else
54-
logger.done("VSCode extension installed via VSIX")
55-
if output and output ~= "" then
56-
-- Print relevant success messages from VSCode
57-
for line in output:gmatch("[^\n]+") do
58-
if line:match("successfully installed") or line:match("Extension.*installed") then
59-
print(" " .. line)
60-
end
99+
package_data.name = ext_id
100+
package_data.displayName = ext_id
101+
package_data.description = ""
102+
package_data.version = "1.0.0"
103+
package_data.publisher = "unknown"
104+
package_data.categories = ""
105+
package_data.keywords = ""
106+
package_data.icon = ""
107+
package_data.license = ""
108+
package_data.engine = "^1.74.0"
109+
end
110+
111+
-- Create [Content_Types].xml with common file types for VSCode extensions
112+
local content_types = [[<?xml version="1.0" encoding="utf-8"?>
113+
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension=".json" ContentType="application/json"/><Default Extension=".vsixmanifest" ContentType="text/xml"/><Default Extension=".md" ContentType="text/markdown"/><Default Extension=".js" ContentType="application/javascript"/><Default Extension=".ts" ContentType="application/typescript"/><Default Extension=".html" ContentType="text/html"/><Default Extension=".css" ContentType="text/css"/><Default Extension=".scss" ContentType="text/css"/><Default Extension=".less" ContentType="text/css"/><Default Extension=".xml" ContentType="text/xml"/><Default Extension=".yaml" ContentType="text/yaml"/><Default Extension=".yml" ContentType="text/yaml"/><Default Extension=".txt" ContentType="text/plain"/><Default Extension=".log" ContentType="text/plain"/><Default Extension=".py" ContentType="text/plain"/><Default Extension=".go" ContentType="text/plain"/><Default Extension=".java" ContentType="text/plain"/><Default Extension=".c" ContentType="text/plain"/><Default Extension=".cpp" ContentType="text/plain"/><Default Extension=".h" ContentType="text/plain"/><Default Extension=".hpp" ContentType="text/plain"/><Default Extension=".rs" ContentType="text/plain"/><Default Extension=".php" ContentType="text/plain"/><Default Extension=".rb" ContentType="text/plain"/><Default Extension=".sh" ContentType="text/plain"/><Default Extension=".png" ContentType="image/png"/><Default Extension=".jpg" ContentType="image/jpeg"/><Default Extension=".jpeg" ContentType="image/jpeg"/><Default Extension=".gif" ContentType="image/gif"/><Default Extension=".svg" ContentType="image/svg+xml"/><Default Extension=".ico" ContentType="image/x-icon"/><Default Extension=".ttf" ContentType="font/ttf"/><Default Extension=".woff" ContentType="font/woff"/><Default Extension=".woff2" ContentType="font/woff2"/><Default Extension=".eot" ContentType="application/vnd.ms-fontobject"/></Types>]]
114+
115+
shell.exec('cat > "%s/[Content_Types].xml" << \'EOF\'\n%sEOF', temp_dir, content_types)
116+
117+
-- Determine icon and license paths
118+
local icon_path = ""
119+
local license_path = ""
120+
121+
if package_data.icon ~= "" then
122+
icon_path = "extension/" .. package_data.icon
123+
else
124+
-- Look for common icon files
125+
local common_icons = {"icon.png", "images/icon.png", "media/icon.png", "assets/icon.png"}
126+
for _, icon_file in ipairs(common_icons) do
127+
local ok, _ = shell.try_exec('test -f "%s"', ext_path .. "/" .. icon_file)
128+
if ok then
129+
icon_path = "extension/" .. icon_file
130+
break
61131
end
62132
end
63-
return true, "installed"
64133
end
134+
135+
-- Look for license files
136+
local common_licenses = {"LICENSE", "LICENSE.txt", "LICENSE.md", "license", "license.txt", "license.md"}
137+
for _, license_file in ipairs(common_licenses) do
138+
local ok, _ = shell.try_exec('test -f "%s"', ext_path .. "/" .. license_file)
139+
if ok then
140+
license_path = "extension/" .. license_file
141+
break
142+
end
143+
end
144+
145+
-- Clean up categories and keywords
146+
local categories = package_data.categories:gsub('"', ''):gsub('%s*,%s*', ',')
147+
local tags = package_data.keywords:gsub('"', ''):gsub('%s*,%s*', ',')
148+
149+
-- Create extension.vsixmanifest
150+
local vsix_manifest = string.format([[<?xml version="1.0" encoding="utf-8"?>
151+
<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
152+
<Metadata>
153+
<Identity Language="en-US" Id="%s" Version="%s" Publisher="%s" />
154+
<DisplayName>%s</DisplayName>
155+
<Description xml:space="preserve">%s</Description>
156+
<Tags>%s</Tags>
157+
<Categories>%s</Categories>
158+
<GalleryFlags>Public</GalleryFlags>
159+
160+
<Properties>
161+
<Property Id="Microsoft.VisualStudio.Code.Engine" Value="%s" />
162+
<Property Id="Microsoft.VisualStudio.Code.ExtensionDependencies" Value="" />
163+
<Property Id="Microsoft.VisualStudio.Code.ExtensionPack" Value="" />
164+
<Property Id="Microsoft.VisualStudio.Code.ExtensionKind" Value="workspace" />
165+
<Property Id="Microsoft.VisualStudio.Code.LocalizedLanguages" Value="" />
166+
167+
<Property Id="Microsoft.VisualStudio.Services.Links.Source" Value="" />
168+
<Property Id="Microsoft.VisualStudio.Services.Links.Getstarted" Value="" />
169+
<Property Id="Microsoft.VisualStudio.Services.Links.GitHub" Value="" />
170+
<Property Id="Microsoft.VisualStudio.Services.Links.Support" Value="" />
171+
<Property Id="Microsoft.VisualStudio.Services.Links.Learn" Value="" />
172+
<Property Id="Microsoft.VisualStudio.Services.Branding.Color" Value="#F2F2F2" />
173+
<Property Id="Microsoft.VisualStudio.Services.Branding.Theme" Value="light" />
174+
<Property Id="Microsoft.VisualStudio.Services.GitHubFlavoredMarkdown" Value="true" />
175+
<Property Id="Microsoft.VisualStudio.Services.Content.Pricing" Value="Free"/>
176+
177+
178+
179+
</Properties>%s%s
180+
</Metadata>
181+
<Installation>
182+
<InstallationTarget Id="Microsoft.VisualStudio.Code"/>
183+
</Installation>
184+
<Dependencies/>
185+
<Assets>
186+
<Asset Type="Microsoft.VisualStudio.Code.Manifest" Path="extension/package.json" Addressable="true" />%s%s%s
187+
</Assets>
188+
</PackageManifest>]],
189+
package_data.name, package_data.version, package_data.publisher, package_data.displayName, package_data.description,
190+
tags, categories, package_data.engine,
191+
license_path ~= "" and string.format('\n\t\t\t<License>%s</License>', license_path) or "",
192+
icon_path ~= "" and string.format('\n\t\t\t<Icon>%s</Icon>', icon_path) or "",
193+
(function() local ok, _ = shell.try_exec('test -f "%s"', ext_path .. "/README.md"); return ok end)() and '\n\t\t\t<Asset Type="Microsoft.VisualStudio.Services.Content.Details" Path="extension/README.md" Addressable="true" />' or "",
194+
(function() local ok, _ = shell.try_exec('test -f "%s"', ext_path .. "/CHANGELOG.md"); return ok end)() and '\n\t\t\t<Asset Type="Microsoft.VisualStudio.Services.Content.Changelog" Path="extension/CHANGELOG.md" Addressable="true" />' or "",
195+
license_path ~= "" and string.format('\n\t\t\t<Asset Type="Microsoft.VisualStudio.Services.Content.License" Path="%s" Addressable="true" />', license_path) or ""
196+
)
197+
198+
-- Add icon asset if found
199+
if icon_path ~= "" then
200+
vsix_manifest = vsix_manifest:gsub("</Assets>", string.format('\t\t\t<Asset Type="Microsoft.VisualStudio.Services.Icons.Default" Path="%s" Addressable="true" />\n\t\t</Assets>', icon_path))
201+
end
202+
203+
shell.exec('cat > "%s/extension.vsixmanifest" << \'EOF\'\n%sEOF', temp_dir, vsix_manifest)
65204
end
66205

67206
-- VSIX file creation and installation
@@ -71,9 +210,20 @@ function M.create_and_install_vsix(ext_id, nix_store_path, install_dir, tool_nam
71210
local vsix_name = (tool_name or ext_id):gsub("%.", "-") .. ".vsix"
72211
local vsix_path = install_dir .. "/" .. vsix_name
73212

74-
-- Create VSIX file
213+
-- Create VSIX file with proper structure
75214
local zip_ok = pcall(function()
76-
shell.exec('cd "%s" && zip -r "%s" * -x "*.DS_Store"', ext_path, vsix_path)
215+
-- Create temp directory for proper VSIX structure
216+
local temp_dir = install_dir .. "/vsix_temp"
217+
shell.exec('mkdir -p "%s/extension"', temp_dir)
218+
shell.exec('cp -r "%s"/. "%s/extension/"', ext_path, temp_dir)
219+
-- Fix permissions on copied files so they can be deleted
220+
shell.exec('chmod -R u+w "%s"', temp_dir)
221+
222+
-- Create required VSIX manifest files
223+
M.create_vsix_manifest(temp_dir, ext_id, ext_path)
224+
225+
shell.exec('cd "%s" && zip -r "%s" . -x "*.DS_Store"', temp_dir, vsix_path)
226+
shell.exec('rm -rf "%s"', temp_dir)
77227
end)
78228

79229
if not zip_ok then

metadata.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
PLUGIN = {
22
name = "mise-nix",
3-
version = "0.11.0",
3+
version = "0.11.1",
44
description = "A backend plugin for Mise that allows you to install and manage packages using Nix with VSCode extension support",
55
author = "jbadeau"
66
}

0 commit comments

Comments
 (0)