aboutsummaryrefslogtreecommitdiff
path: root/src/lpm.lua
diff options
context:
space:
mode:
Diffstat (limited to 'src/lpm.lua')
-rw-r--r--src/lpm.lua203
1 files changed, 118 insertions, 85 deletions
diff --git a/src/lpm.lua b/src/lpm.lua
index 3ee5cc1..9cf0bbb 100644
--- a/src/lpm.lua
+++ b/src/lpm.lua
@@ -358,6 +358,7 @@ local function is_commit_hash(hash)
end
+
local common = {}
function common.merge(dst, src) for k, v in pairs(src) do dst[k] = v end return dst end
function common.map(l, p) local t = {} for i, v in ipairs(l) do table.insert(t, p(v)) end return t end
@@ -455,6 +456,24 @@ end
local HOME, USERDIR, CACHEDIR, JSON, VERBOSE, MOD_VERSION, QUIET, FORCE, AUTO_PULL_REMOTES, ARCH, ASSUME_YES, NO_INSTALL_OPTIONAL, TMPDIR, DATADIR, BINARY, POST, repositories, lite_xls, system_bottle, progress_bar_label, write_progress_bar
+
+local status = 0
+local function error_handler(err)
+ local s, e = err and err:find(":%d+")
+ local message = e and err:sub(e + 3) or err
+ if JSON then
+ if VERBOSE then
+ io.stderr:write(json.encode({ error = err, actions = actions, warnings = warnings, traceback = debug.traceback() }) .. "\n")
+ else
+ io.stderr:write(json.encode({ error = message or err, actions = actions, warnings = warnings }) .. "\n")
+ end
+ else
+ if err then io.stderr:write((not VERBOSE and message or err) .. "\n") end
+ if VERBOSE then io.stderr:write(debug.traceback() .. "\n") end
+ end
+ status = -1
+end
+
local function engage_locks(func, err)
if not system.stat(USERDIR) then common.mkdirp(USERDIR) end
local lockfile = USERDIR .. PATHSEP .. ".lock"
@@ -561,12 +580,19 @@ function Addon.new(repository, metadata)
version = "1.0",
dependencies = {},
conflicts = {},
- local_path = repository and not metadata.remote and (repository.local_path .. PATHSEP .. (repository.commit or repository.branch) .. (metadata.path and (PATHSEP .. metadata.path:gsub("^/", "")) or "")) or nil,
name = metadata.id
}, metadata), Addon)
self.type = type
-- Directory.
self.organization = metadata.organization or (((self.files and #self.files > 0) or self.remote or (not self.path and not self.url)) and "complex" or "singleton")
+ if not self.local_path and repository then
+ if metadata.remote then
+ local local_path = (Repository.url(metadata.remote).local_path .. (metadata.path and (PATHSEP .. metadata.path:gsub("^/", "")) or ""))
+ self.local_path = system.stat(local_path) and (Repository.url(metadata.remote).local_path .. (metadata.path and (PATHSEP .. metadata.path:gsub("^/", "")) or "")) or nil
+ else
+ self.local_path = (repository.local_path .. (metadata.path and (PATHSEP .. metadata.path:gsub("^/", "")) or "")) or nil
+ end
+ end
return self
end
@@ -578,7 +604,7 @@ function Addon.is_path_different(path1, path2)
local stat1, stat2 = system.stat(path1), system.stat(path2)
if not stat1 or not stat2 or stat1.type ~= stat2.type or stat1.size ~= stat2.size then return true end
if stat1.type == "dir" then
- for i, file in ipairs(system.ls(path1)) do if Addon.is_path_different(path1 .. PATHSEP .. file, path2 .. PATHSEP.. file) then return true end end
+ for i, file in ipairs(system.ls(path1)) do if not common.basename(file):find("^%.") and Addon.is_path_different(path1 .. PATHSEP .. file, path2 .. PATHSEP.. file) then return true end end
return false
else
return system.hash(path1, "file") ~= system.hash(path2, "file")
@@ -605,8 +631,9 @@ function Addon:is_installed(bottle)
if self:is_core(bottle) or self:is_bundled(bottle) or not self.repository then return true end
local install_path = self:get_install_path(bottle)
if not system.stat(install_path) then return false end
- if #common.grep({ bottle:get_addon(self.id, nil, { }) }, function(addon) return not addon.repository end) > 0 then return false end
- return not Addon.is_addon_different(self.local_path, install_path)
+ local installed_addons = common.grep({ bottle:get_addon(self.id, nil, { }) }, function(addon) return not addon.repository end)
+ if #installed_addons > 0 then return false end
+ return self.local_path and not Addon.is_addon_different(self.local_path, install_path)
end
function Addon:is_upgradable(bottle)
if self:is_installed(bottle) then
@@ -698,11 +725,8 @@ function Addon:install(bottle, installing)
if system.hash(path, "file") ~= self.checksum then fatal_warning("checksum doesn't match for " .. path) end
elseif self.remote then
log_progress_action("Fetching repository " .. self.remote .. " into " .. install_path)
- common.mkdirp(temporary_install_path)
- local _, _, url, branch = self.remote:find("^(.*):(.*)$")
- system.init(temporary_install_path, url)
- system.fetch(temporary_install_path, write_progress_bar)
- common.reset(temporary_install_path, branch, "hard")
+ local repo = Repository.url(self.remote):fetch()
+ common.copy(repo.local_path, temporary_install_path)
common.rmrf(temporary_install_path .. PATHSEP .. ".git")
common.rmrf(temporary_install_path .. PATHSEP .. ".gitignore")
elseif self.path then
@@ -782,20 +806,21 @@ function Repository.new(hash)
remote = hash.remote,
branch = hash.branch,
addons = nil,
+ repo_path = CACHEDIR .. PATHSEP .. "repos" .. PATHSEP .. system.hash(hash.remote),
lite_xls = {},
- local_path = CACHEDIR .. PATHSEP .. "repos" .. PATHSEP .. system.hash(hash.remote),
last_retrieval = nil
}, Repository)
- if system.stat(self.local_path) and not self.commit and not self.branch then
+ if system.stat(self.repo_path) and not self.commit and not self.branch then
-- In the case where we don't have a branch, and don't have a commit, check for the presence of `master` and `main`.
- if system.stat(self.local_path .. PATHSEP .. "master") then
+ if system.stat(self.repo_path .. PATHSEP .. "master") then
self.branch = "master"
- elseif system.stat(self.local_path .. PATHSEP .. "main") then
+ elseif system.stat(self.repo_path .. PATHSEP .. "main") then
self.branch = "main"
else
- error("can't find branch for " .. self.remote)
+ error("can't find branch for " .. self.remote .. " in " .. self.repo_path)
end
end
+ self.local_path = self.repo_path .. PATHSEP .. (self.commit or self.branch)
return self
end
@@ -813,8 +838,8 @@ end
function Repository:parse_manifest(already_pulling)
if self.manifest then return self.manifest, self.remotes end
- if system.stat(self.local_path) and system.stat(self.local_path .. PATHSEP .. (self.commit or self.branch)) then
- self.manifest_path = self.local_path .. PATHSEP .. (self.commit or self.branch) .. PATHSEP .. "manifest.json"
+ if system.stat(self.local_path) then
+ self.manifest_path = self.local_path .. PATHSEP .. "manifest.json"
if not system.stat(self.manifest_path) then
log_action("Can't find manifest.json for " .. self:url() .. "; automatically generating manifest.")
self:generate_manifest()
@@ -859,7 +884,7 @@ end
-- assuming each .lua file under the `addons` folder is a addon. also parse the README, if present, and see if any of the addons
function Repository:generate_manifest()
if not self.commit and not self.branch then error("requires an instantiation") end
- local path = self.local_path .. PATHSEP .. (self.commit or self.branch)
+ local path = self.local_path
local addons, addon_map = {}, {}
for _, folder in ipairs({ "plugins", "colors", "libraries" }) do
local addon_dir = system.stat(path .. PATHSEP .. folder) and PATHSEP .. folder .. PATHSEP or PATHSEP
@@ -879,7 +904,7 @@ function Repository:generate_manifest()
addon_map[id].remote = path
pcall(function()
local repo = Repository.url(path):add()
- addon_map[id].remote = path .. ":" .. system.revparse(repo.local_path .. PATHSEP .. (repo.branch))
+ addon_map[id].remote = path .. ":" .. system.revparse(repo.local_path)
end)
end
else
@@ -916,34 +941,56 @@ function Repository:generate_manifest()
common.write(path .. PATHSEP .. "manifest.json", json.encode({ addons = addons }))
end
-function Repository:add(pull_remotes)
- -- If neither specified then pull onto `master`, and check the main branch name, and move if necessary.
- if not self.branch and not self.commit then
- local path = self.local_path .. PATHSEP .. "master"
- common.mkdirp(path)
- log_progress_action("Fetching " .. self.remote .. ":master/main...")
- system.init(path, self.remote)
- system.fetch(path, write_progress_bar)
- if not pcall(system.reset, path, "refs/remotes/origin/master", "hard") then
- if pcall(system.reset, path, "refs/remotes/origin/main", "hard") then
- common.rename(path, self.local_path .. PATHSEP .. "main")
- self.branch = "main"
+-- useds to fetch things from a generic place
+function Repository:fetch()
+ local path
+ local status, err = pcall(function()
+ if not self.branch and not self.commit then
+ path = self.repo_path .. PATHSEP .. "master"
+ common.mkdirp(path)
+ log_progress_action("Fetching " .. self.remote .. ":master/main...")
+ system.init(path, self.remote)
+ system.fetch(path, write_progress_bar)
+ if not pcall(system.reset, path, "refs/remotes/origin/master", "hard") then
+ if pcall(system.reset, path, "refs/remotes/origin/main", "hard") then
+ common.rename(path, self.repo_path .. PATHSEP .. "main")
+ path = self.repo_path .. PATHSEP .. "main"
+ self.branch = "main"
+ else
+ error("can't find master or main.")
+ end
else
- error("can't find master or main.")
+ self.branch = "master"
end
+ self.local_path = path
else
- self.branch = "master"
+ path = self.local_path
+ local exists = system.stat(path)
+ if not exists then
+ common.mkdirp(path)
+ system.init(path, self.remote)
+ end
+ if not exists or self.branch then
+ log_progress_action("Fetching " .. self.remote .. ":" .. (self.commit or self.branch) .. "...")
+ system.fetch(path, write_progress_bar)
+ common.reset(path, self.commit or self.branch, "hard")
+ end
+ self.manifest = nil
end
- else
- local path = self.local_path .. PATHSEP .. (self.commit or self.branch)
- common.mkdirp(path)
- log_progress_action("Fetching " .. self.remote .. ":" .. (self.commit or self.branch) .. "...")
- system.init(path, self.remote)
- system.fetch(path, write_progress_bar)
- common.reset(path, self.commit or self.branch, "hard")
- self.manifest = nil
+ end)
+ if not status then
+ if path then
+ common.rmrf(path)
+ if #system.ls(common.dirname(path)) == 0 then common.rmrf(common.dirname(path)) end
+ end
+ error(err)
end
- local manifest, remotes = self:parse_manifest()
+ return self
+end
+
+function Repository:add(pull_remotes)
+ -- If neither specified then pull onto `master`, and check the main branch name, and move if necessary.
+ local manifest, remotes = self:fetch():parse_manifest()
if pull_remotes then -- any remotes we don't have in our listing, call add, and add into the list
for i, remote in ipairs(remotes) do
if not common.first(repositories, function(repo) return repo.remote == remote.remote and repo.branch == remote.branch and repo.commit == remote.commit end) then
@@ -959,8 +1006,7 @@ end
function Repository:update(pull_remotes)
local manifest, remotes = self:parse_manifest()
if self.branch then
- local path = self.local_path .. PATHSEP .. self.branch
- system.fetch(path)
+ system.fetch(self.local_path)
common.reset(path, self.branch, "hard")
log_action("Updated " .. self:url())
self.manifest = nil
@@ -978,8 +1024,8 @@ end
function Repository:remove()
- common.rmrf(self.local_path .. PATHSEP .. (self.commit or self.branch))
- if #system.ls(self.local_path) == 0 then common.rmrf(self.local_path) end
+ common.rmrf(self.local_path)
+ if #system.ls(self.repo_path) == 0 then common.rmrf(self.repo_path) end
end
@@ -1124,24 +1170,29 @@ end
function Bottle:all_addons()
if self.all_addons_cache then return self.all_addons_cache end
local t, hash = get_repository_addons()
- local addon_paths = {
- (self.local_path and (self.local_path .. PATHSEP .. "user") or USERDIR) .. PATHSEP .. "plugins",
- self.lite_xl.datadir_path .. PATHSEP .. "plugins"
- }
- for i, addon_path in ipairs(common.grep(addon_paths, function(e) return system.stat(e) end)) do
- for j, v in ipairs(system.ls(addon_path)) do
- local id = v:gsub("%.lua$", ""):lower():gsub("[^a-z0-9%-_]", "")
- local path = addon_path .. PATHSEP .. v
- local matching = hash[id] and common.grep(hash[id], function(e) return e.local_path and not Addon.is_addon_different(e.local_path, path) end)[1]
- if i == 2 or not hash[id] or not matching then
- table.insert(t, Addon.new(nil, {
- id = id,
- type = i == 2 and (hash[id] and "bundled" or "core"),
- organization = (v:find("%.lua$") and "singleton" or "complex"),
- mod_version = self.lite_xl.mod_version,
- path = "plugins" .. PATHSEP .. v,
- description = (hash[id] and hash[id][1].description or nil)
- }))
+ for _, addon_type in ipairs({ "plugins", "libraries" }) do
+ local addon_paths = {
+ (self.local_path and (self.local_path .. PATHSEP .. "user") or USERDIR) .. PATHSEP .. addon_type,
+ self.lite_xl.datadir_path .. PATHSEP .. addon_type
+ }
+ for i, addon_path in ipairs(common.grep(addon_paths, function(e) return system.stat(e) end)) do
+ for j, v in ipairs(system.ls(addon_path)) do
+ local id = v:gsub("%.lua$", ""):lower():gsub("[^a-z0-9%-_]", "")
+ local path = addon_path .. PATHSEP .. v
+ local matching = hash[id] and common.grep(hash[id], function(e)
+ return e.local_path and not Addon.is_addon_different(e.local_path, path)
+ end)[1]
+ if i == 2 or not hash[id] or not matching then
+ table.insert(t, Addon.new(nil, {
+ id = id,
+ type = (addon_type == "plugins" and (i == 2 and (hash[id] and "bundled" or "core") or "plugin") or "library"),
+ organization = (v:find("%.lua$") and "singleton" or "complex"),
+ local_path = path,
+ mod_version = self.lite_xl.mod_version,
+ path = addon_type .. PATHSEP .. v,
+ description = (hash[id] and hash[id][1].description or nil)
+ }))
+ end
end
end
end
@@ -1174,7 +1225,6 @@ function Bottle:get_addon(id, version, filter)
return table.unpack(common.sort(common.uniq(candidates), function (a,b) return a.version < b.version end))
end
-
local function get_repository(url)
if not url then error("requires a repository url") end
local r = Repository.url(url)
@@ -1197,7 +1247,7 @@ local function lpm_repo_init()
DEFAULT_REPOS = { Repository.url("https://github.com/adamharrison/lite-xl-plugin-manager.git:latest") }
if not system.stat(CACHEDIR .. PATHSEP .. "repos") then
for i, repository in ipairs(DEFAULT_REPOS) do
- if not system.stat(repository.local_path) or not system.stat(repository.local_path .. PATHSEP .. (repository.commit or repository.branch)) then
+ if not system.stat(repository.local_path) then
table.insert(repositories, repository:add(true))
end
end
@@ -1385,7 +1435,7 @@ local function lpm_install(type, ...)
local potential_addons = { system_bottle:get_addon(id, version, { mod_version = system_bottle.lite_xl.mod_version }) }
local addons = common.grep(potential_addons, function(e) return not e:is_installed(system_bottle) end)
if #addons == 0 and #potential_addons == 0 then error("can't find addon " .. id .. " mod-version: " .. (system_bottle.lite_xl.mod_version or 'any')) end
- if #addons == 0 then error("addon " .. id .. " already installed") end
+ if #addons == 0 then error("addon " .. id .. " already installed") end
for j,v in ipairs(addons) do v:install(system_bottle) end
end
end
@@ -1406,13 +1456,13 @@ local function lpm_addon_reinstall(type, ...) for i, id in ipairs({ ... }) do pc
local function lpm_repo_list()
if JSON then
- io.stdout:write(json.encode({ repositories = common.map(repositories, function(repo) return { remote = repo.remote, commit = repo.commit, branch = repo.branch, path = repo.local_path .. PATHSEP .. (repo.commit or repo.branch), remotes = common.map(repo.remotes or {}, function(r) return r:url() end) } end) }) .. "\n")
+ io.stdout:write(json.encode({ repositories = common.map(repositories, function(repo) return { remote = repo.remote, commit = repo.commit, branch = repo.branch, path = repo.local_path, remotes = common.map(repo.remotes or {}, function(r) return r:url() end) } end) }) .. "\n")
else
for i, repository in ipairs(repositories) do
local _, remotes = repository:parse_manifest()
if i ~= 0 then print("---------------------------") end
print("Remote : " .. repository:url())
- print("Path : " .. repository.local_path .. PATHSEP .. (repository.commit or repository.branch))
+ print("Path : " .. repository.local_path)
print("Remotes: " .. json.encode(common.map(repository.remotes or {}, function(r) return r:url() end)))
end
end
@@ -1526,22 +1576,6 @@ local function parse_arguments(arguments, options)
return args
end
-local status = 0
-local function error_handler(err)
- local s, e = err and err:find(":%d+")
- local message = e and err:sub(e + 3) or err
- if JSON then
- if VERBOSE then
- io.stderr:write(json.encode({ error = err, actions = actions, warnings = warnings, traceback = debug.traceback() }) .. "\n")
- else
- io.stderr:write(json.encode({ error = message or err, actions = actions, warnings = warnings }) .. "\n")
- end
- else
- if err then io.stderr:write((not VERBOSE and message or err) .. "\n") end
- if VERBOSE then io.stderr:write(debug.traceback() .. "\n") end
- end
- status = -1
-end
local function run_command(ARGS)
if not ARGS[2]:find("%S") then return
@@ -1910,7 +1944,6 @@ in any circumstance unless explicitly supplied.
actions, warnings = {}, {}
end
end
-
end, error_handler)