summaryrefslogtreecommitdiff
path: root/xmake/plugins/plugin/main.lua
diff options
context:
space:
mode:
authorSaikari <[email protected]>2026-07-27 12:36:07 +0300
committerruki <[email protected]>2026-07-30 10:17:32 +0800
commit235b82edd8b262a7080a9f9ee518567f5a54b836 (patch)
treefedc3989bf614fcceb238df1107a4fa516870170 /xmake/plugins/plugin/main.lua
parent9eaaccdbd7483ff7a9fc1f4cdbd91057c62ecbc9 (diff)
Enhance plugin installation by validating plugin names and improving error handling during installation and updates
Diffstat (limited to 'xmake/plugins/plugin/main.lua')
-rw-r--r--xmake/plugins/plugin/main.lua266
1 files changed, 155 insertions, 111 deletions
diff --git a/xmake/plugins/plugin/main.lua b/xmake/plugins/plugin/main.lua
index a29ad8687..db03c7abb 100644
--- a/xmake/plugins/plugin/main.lua
+++ b/xmake/plugins/plugin/main.lua
@@ -26,10 +26,16 @@ import("devel.git")
import("net.fasturl")
import("private.action.require.impl.environment")
+-- validate a plugin directory name
+function _check_plugin_name(name)
+ assert(type(name) == "string" and name ~= "" and name ~= "." and not name:find("..", 1, true) and not name:find("[/\\:]"), "invalid plugin name(%s)!", name)
+ return name
+end
+
-- get plugin directory in ~/.xmake/plugins
function _get_plugindir(name)
local plugindir = path.join(global.directory(), "plugins")
- return name and path.join(plugindir, name) or plugindir
+ return name and path.join(plugindir, _check_plugin_name(name)) or plugindir
end
-- get local and global repositories, with local taking precedence
@@ -39,22 +45,36 @@ end
-- get plugin urls for batch install
function _plugin_urls()
- local urls = option.get("plugins")
- if urls then
- local result = {}
- for _, url in ipairs(urls) do
- table.insert(result, git.asgiturl(url) or url)
- end
- urls = result
- else
- urls = {
- "https://github.com/xmake-io/xmake-plugins.git",
- "https://gitlab.com/tboox/xmake-plugins.git",
- "https://gitee.com/tboox/xmake-plugins.git"}
- urls = fasturl.add(urls)
- urls = fasturl.sort(urls)
- end
- return urls
+ local urls = {
+ "https://github.com/xmake-io/xmake-plugins.git",
+ "https://gitlab.com/tboox/xmake-plugins.git",
+ "https://gitee.com/tboox/xmake-plugins.git"}
+ fasturl.add(urls)
+ return fasturl.sort(urls)
+end
+
+-- run a function with a temporary directory that is removed on success or failure
+function _with_tmpdir(fn)
+ local tmpdir = os.tmpfile() .. ".dir"
+ return try
+ {
+ function ()
+ return fn(tmpdir)
+ end,
+ catch
+ {
+ function (errors)
+ os.tryrm(tmpdir)
+ raise(errors)
+ end
+ },
+ finally
+ {
+ function ()
+ os.tryrm(tmpdir)
+ end
+ }
+ }
end
function _manifest_path()
@@ -84,6 +104,7 @@ end
-- install a plugin from the given repository or the first repository containing it
function _install_plugins_from_repo(name, reponame)
+ _check_plugin_name(name)
for _, repo in ipairs(_repositories()) do
if not reponame or repo:name() == reponame then
local srcdir = _find_plugin_in_repo(repo:directory(), name)
@@ -124,32 +145,65 @@ function _install_from_git(url)
end
url = "https://github.com/" .. url .. ".git"
end
- local tmpdir = os.tmpfile() .. ".dir"
- local clone_opt = {verbose = option.get("verbose"), outputdir = tmpdir}
- if branch then
- clone_opt.branch = branch
- end
- git.clone(git.asgiturl(url) or url, clone_opt)
- local found = false
- local function install(srcdir, name)
- local dstdir = _get_plugindir(name)
- assert(not os.isdir(dstdir), "plugin(%s) already exists!", name)
- os.vcp(srcdir, dstdir)
- cprint(" ${color.success}-> ${bright}%s${clear}", name)
- found = true
- end
- if os.isfile(path.join(tmpdir, "xmake.lua")) then
- install(tmpdir, path.basename(path.filename(url)))
- else
- for _, filepath in ipairs(os.files(path.join(tmpdir, "*", "xmake.lua"))) do
- local srcdir = path.directory(filepath)
- install(srcdir, path.filename(srcdir))
+ _with_tmpdir(function (tmpdir)
+ local clone_opt = {verbose = option.get("verbose"), outputdir = tmpdir}
+ if branch then
+ clone_opt.branch = branch
+ end
+ git.clone(git.asgiturl(url) or url, clone_opt)
+ local found = false
+ local function install(srcdir, name)
+ local dstdir = _get_plugindir(name)
+ assert(not os.isdir(dstdir), "plugin(%s) already exists!", name)
+ os.vcp(srcdir, dstdir)
+ cprint(" ${color.success}-> ${bright}%s${clear}", name)
+ found = true
end
+ if os.isfile(path.join(tmpdir, "xmake.lua")) then
+ install(tmpdir, path.basename(path.filename(url)))
+ else
+ for _, filepath in ipairs(os.files(path.join(tmpdir, "*", "xmake.lua"))) do
+ local srcdir = path.directory(filepath)
+ install(srcdir, path.filename(srcdir))
+ end
+ end
+ if not found then
+ raise("no plugin found in %s", url)
+ end
+ end)
+end
+
+-- install a single plugin
+function _install_one(name)
+ -- parse repo@plugin format
+ local i = name:find("@", 1, true)
+ if i and not name:find("[/\\:]") then
+ local reponame = name:sub(1, i - 1)
+ local pluginname = name:sub(i + 1)
+ _install_plugins_from_repo(pluginname, reponame)
+ return
end
- os.tryrm(tmpdir)
- if not found then
- raise("no plugin found in %s", url)
+
+ -- github shortcut: github:user/repo or github:user/repo#branch
+ if name:startswith("github:") then
+ _install_from_git(name)
+ return
+ end
+
+ -- git url or local path
+ if name:startswith("file://") or git.asgiturl(name) then
+ _install_from_git(name)
+ return
+ elseif os.isdir(name) then
+ _install_from_local(name)
+ return
+ elseif name:find("[/\\:]") then
+ _install_from_git(name)
+ return
end
+
+ -- plain name: try to find it in repositories
+ _install_plugins_from_repo(name)
end
-- install plugins
@@ -158,84 +212,69 @@ function _install()
-- enter environment
environment.enter()
+ local errors
try
{
function ()
- -- do install
- local name = option.get("plugins")
- if name and #name > 0 then
- -- parse repo@plugin format
- local i = name:find("@", 1, true)
- if i and not name:find("[/\\:]") then
- local reponame = name:sub(1, i - 1)
- local pluginname = name:sub(i + 1)
- _install_plugins_from_repo(pluginname, reponame)
- return
- end
-
- -- github shortcut: github:user/repo or github:user/repo#branch
- if name:startswith("github:") then
- _install_from_git(name)
- return
- end
-
- -- git url or local path
- if name:startswith("file://") or git.asgiturl(name) then
- _install_from_git(name)
- return
- elseif os.isdir(name) then
- _install_from_local(name)
- return
- elseif name:find("[/\\:]") then
- _install_from_git(name)
- return
+ -- install requested plugins
+ local names = option.get("plugins")
+ if names then
+ for _, name in ipairs(names) do
+ _install_one(name)
end
-
- -- plain name: try to find it in repositories
- _install_plugins_from_repo(name)
return
end
-- do batch install from plugin collection urls
local urls = _plugin_urls()
- local tmpdir = os.tmpfile() .. ".dir"
local plugindir = _get_plugindir()
local installed_url
- for _, url in ipairs(urls) do
- cprint("installing plugins from ${bright}%s${clear} ..", url)
- git.clone(url, {verbose = option.get("verbose"), outputdir = tmpdir})
- installed_url = url
- break
- end
- for _, filepath in ipairs(os.files(path.join(tmpdir, "*", "xmake.lua"))) do
- local srcdir = path.directory(filepath)
- local name = path.filename(srcdir)
- local dstdir = _get_plugindir(name)
- assert(not os.isdir(dstdir), "plugin(%s) already exists!", name)
- os.vcp(srcdir, dstdir)
- cprint(" ${color.success}-> ${bright}%s${clear}", name)
- end
- os.tryrm(tmpdir)
-
- if installed_url then
- local manifest = _load_manifest() or {}
- manifest.urls = manifest.urls or {}
- table.join2(manifest.urls, installed_url)
- _save_manifest(manifest)
- end
+ _with_tmpdir(function (tmpdir)
+ for _, url in ipairs(urls) do
+ cprint("installing plugins from ${bright}%s${clear} ..", url)
+ local ok = try
+ {
+ function ()
+ git.clone(url, {verbose = option.get("verbose"), outputdir = tmpdir})
+ return true
+ end
+ }
+ if ok then
+ installed_url = url
+ break
+ end
+ os.tryrm(tmpdir)
+ end
+ assert(installed_url, "failed to install plugins from all urls!")
+ for _, filepath in ipairs(os.files(path.join(tmpdir, "*", "xmake.lua"))) do
+ local srcdir = path.directory(filepath)
+ local name = path.filename(srcdir)
+ local dstdir = _get_plugindir(name)
+ assert(not os.isdir(dstdir), "plugin(%s) already exists!", name)
+ os.vcp(srcdir, dstdir)
+ cprint(" ${color.success}-> ${bright}%s${clear}", name)
+ end
+ end)
+ local manifest = _load_manifest() or {}
+ manifest.urls = manifest.urls or {}
+ table.join2(manifest.urls, installed_url)
+ _save_manifest(manifest)
cprint("${color.success}all plugins have been installed in ${bright}%s${clear}!", plugindir)
end,
catch
{
- function (errors)
- raise(errors)
+ function (_errors)
+ errors = _errors
end
}
}
-- leave environment
environment.leave()
+ if errors then
+ raise(errors)
+ end
end
-- update plugins
@@ -244,6 +283,7 @@ function _update()
-- enter environment
environment.enter()
+ local errors
try
{
function ()
@@ -255,36 +295,40 @@ function _update()
local plugindir = _get_plugindir()
for _, url in ipairs(urls) do
cprint("updating plugins from ${bright}%s${clear} ..", url)
- local tmpdir = os.tmpfile() .. ".dir"
- git.clone(url, {verbose = option.get("verbose"), outputdir = tmpdir})
- for _, filepath in ipairs(os.files(path.join(tmpdir, "*", "xmake.lua"))) do
- local srcdir = path.directory(filepath)
- local name = path.filename(srcdir)
- local dstdir = _get_plugindir(name)
- os.tryrm(dstdir)
- os.vcp(srcdir, dstdir)
- cprint(" ${color.success}-> ${bright}%s${clear}", name)
- end
- os.tryrm(tmpdir)
+ _with_tmpdir(function (tmpdir)
+ git.clone(url, {verbose = option.get("verbose"), outputdir = tmpdir})
+ for _, filepath in ipairs(os.files(path.join(tmpdir, "*", "xmake.lua"))) do
+ local srcdir = path.directory(filepath)
+ local name = path.filename(srcdir)
+ local dstdir = _get_plugindir(name)
+ os.tryrm(dstdir)
+ os.vcp(srcdir, dstdir)
+ cprint(" ${color.success}-> ${bright}%s${clear}", name)
+ end
+ end)
end
cprint("${color.success}all plugins have been updated in ${bright}%s${clear}!", plugindir)
end,
catch
{
- function (errors)
- raise(errors)
+ function (_errors)
+ errors = _errors
end
}
}
-- leave environment
environment.leave()
+ if errors then
+ raise(errors)
+ end
end
-- remove the given installed plugin
function _remove()
- local name = assert(option.get("plugins"), "please specify the plugin name to be removed!")
- assert(name ~= "" and name ~= "." and not name:find("..", 1, true) and not name:find("[/\\:]"), "invalid plugin name(%s)!", name)
+ local names = assert(option.get("plugins"), "please specify the plugin name to be removed!")
+ assert(#names == 1, "please specify only one plugin name to be removed!")
+ local name = _check_plugin_name(names[1])
local dir = _get_plugindir(name)
assert(os.isdir(dir), "plugin(%s) not found!", name)
os.rmdir(dir)