feat: new task pipeline runner

This commit is contained in:
Folke Lemaitre 2022-11-28 11:04:32 +01:00
parent 97f44f9f65
commit ab1b512545
No known key found for this signature in database
GPG key ID: 41F8B1FBACAE2040
7 changed files with 427 additions and 298 deletions

View file

@ -1,36 +1,40 @@
local Process = require("lazy.manage.process")
local Loader = require("lazy.core.loader")
local Util = require("lazy.util")
---@class LazyTaskDef
---@field needed? fun(plugin:LazyPlugin, opts:RunnerOpts):any?
---@field run fun(task:LazyTask)
---@alias LazyTaskState fun():boolean?
---@class LazyTask
---@field plugin LazyPlugin
---@field type TaskType
---@field running boolean
---@field opts TaskOptions
---@field output string
---@field status string
---@field error? string
---@field private _task fun(task:LazyTask)
---@field private _running LazyPluginState[]
---@field private _started boolean
---@field private _opts TaskOptions
local Task = {}
---@alias TaskType "update"|"install"|"run"|"clean"|"log"|"docs"
---@class TaskOptions
---@field on_done? fun(task:LazyTask)
local options = {
log = {
since = "7 days ago",
---@type string
from = nil,
---@type string
to = nil,
},
}
---@param plugin LazyPlugin
---@param type TaskType
---@param opts? TaskOptions
function Task.new(plugin, type, opts)
---@param task fun(task:LazyTask)
function Task.new(plugin, type, task, opts)
local self = setmetatable({}, {
__index = Task,
})
self.opts = vim.tbl_deep_extend("force", {}, options, opts or {})
self._opts = opts or {}
self._running = {}
self._task = task
self._started = false
self.plugin = plugin
self.type = type
self.output = ""
@ -40,10 +44,40 @@ function Task.new(plugin, type, opts)
return self
end
function Task:_done()
self.running = false
if self.opts.on_done then
self.opts.on_done(self)
function Task:has_started()
return self._started
end
function Task:is_done()
return self:has_started() and not self:is_running()
end
function Task:is_running()
for _, state in ipairs(self._running) do
if state() then
return true
end
end
return false
end
function Task:start()
self._started = true
---@type boolean, string|any
local ok, err = pcall(self._task, self)
if not ok then
self.error = err or "failed"
end
self:_check()
end
---@private
function Task:_check()
if self:is_running() then
return
end
if self._opts.on_done then
self._opts.on_done(self)
end
vim.cmd("do User LazyRender")
vim.api.nvim_exec_autocmds("User", {
@ -52,99 +86,25 @@ function Task:_done()
})
end
function Task:clean()
local dir = self.plugin.dir:gsub("/+$", "")
local stat = vim.loop.fs_lstat(dir)
if stat.type == "directory" then
Util.walk(dir, function(path, _, type)
if type == "directory" then
vim.loop.fs_rmdir(path)
else
vim.loop.fs_unlink(path)
end
end)
vim.loop.fs_rmdir(dir)
else
vim.loop.fs_unlink(dir)
end
self.plugin.installed = false
self:_done()
end
function Task:install()
if Util.file_exists(self.plugin.uri) then
vim.loop.fs_symlink(self.plugin.uri, self.plugin.dir, {
dir = true,
})
vim.opt.runtimepath:append(self.plugin.uri)
self:_done()
else
local args = {
"clone",
self.plugin.uri,
-- "--depth=1",
"--filter=blob:none",
-- "--filter=tree:0",
"--recurse-submodules",
"--single-branch",
"--shallow-submodules",
"--progress",
}
if self.plugin.branch then
vim.list_extend(args, {
"-b",
self.plugin.branch,
})
---@param fn fun()
function Task:schedule(fn)
local done = false
table.insert(self._running, function()
return not done
end)
vim.schedule(function()
---@type boolean, string|any
local ok, err = pcall(fn)
if not ok then
self.error = err or "failed"
end
table.insert(args, self.plugin.dir)
self:spawn("git", {
args = args,
on_exit = function(ok)
if ok then
self.plugin.installed = true
self.plugin.dirty = true
end
end,
})
end
end
function Task:run()
Loader.load(self.plugin, { task = "run" }, { load_start = true })
local run = self.plugin.run
if run then
if type(run) == "string" and run:sub(1, 1) == ":" then
local cmd = vim.api.nvim_parse_cmd(run:sub(2), {})
self.output = vim.api.nvim_cmd(cmd, { output = true })
elseif type(run) == "function" then
run()
else
local args = vim.split(run, "%s+")
return self:spawn(table.remove(args, 1), {
args = args,
cwd = self.plugin.dir,
})
end
end
-- FIXME: the spawn above wont be finished yet
self:_done()
end
function Task:docs()
local docs = self.plugin.dir .. "/doc/"
if Util.file_exists(docs) then
self.output = vim.api.nvim_cmd({ cmd = "helptags", args = { docs } }, { output = true })
end
self:_done()
done = true
self:_check()
end)
end
---@param cmd string
---@param opts ProcessOpts
---@param opts? ProcessOpts
function Task:spawn(cmd, opts)
opts = opts or {}
local on_line = opts.on_line
@ -152,116 +112,42 @@ function Task:spawn(cmd, opts)
function opts.on_line(line)
self.status = line
if on_line then
pcall(on_line, line)
end
vim.cmd("do User LazyRender")
end
---@param output string
function opts.on_exit(ok, output)
self.output = output
self.output = self.output .. output
if not ok then
self.error = output
self.error = self.error and (self.error .. "\n" .. output) or output
end
if on_exit then
pcall(on_exit, ok, output)
end
self:_done()
self:_check()
end
Process.spawn(cmd, opts)
end
function Task:start()
self.running = true
local ok, err = pcall(function()
if self.type == "update" then
self:update()
elseif self.type == "install" then
self:install()
elseif self.type == "run" then
self:run()
elseif self.type == "clean" then
self:clean()
elseif self.type == "log" then
self:log()
elseif self.type == "docs" then
self:docs()
end
local proc = Process.spawn(cmd, opts)
table.insert(self._running, function()
return proc and not proc:is_closing()
end)
if not ok then
self.error = err or "failed"
self:_done()
end
end
function Task:log()
if not Util.file_exists(self.plugin.dir .. "/.git") then
self:_done()
return
end
local args = {
"log",
"--pretty=format:%h %s (%cr)",
"--abbrev-commit",
"--decorate",
"--date=short",
"--color=never",
}
if self.opts.log.from then
table.insert(args, self.opts.log.from .. ".." .. (self.opts.log.to or "HEAD"))
else
table.insert(args, "--since=" .. self.opts.log.since)
end
self:spawn("git", {
args = args,
cwd = self.plugin.dir,
})
end
function Task:update()
if Util.file_exists(self.plugin.uri) then
if vim.loop.fs_realpath(self.plugin.uri) ~= vim.loop.fs_realpath(self.plugin.dir) then
vim.loop.fs_unlink(self.plugin.dir)
vim.loop.fs_symlink(self.plugin.uri, self.plugin.dir, {
dir = true,
})
vim.opt.runtimepath:append(self.plugin.uri)
---@param tasks (LazyTask?)[]
function Task.all_done(tasks)
for _, task in ipairs(tasks) do
if task and not task:is_done() then
return false
end
self:_done()
else
local args = {
"pull",
"--tags",
"--recurse-submodules",
"--update-shallow",
"--progress",
}
local git = assert(Util.git_info(self.plugin.dir))
end
return true
end
self:spawn("git", {
args = args,
cwd = self.plugin.dir,
on_exit = function(ok)
if ok then
local git_new = assert(Util.git_info(self.plugin.dir))
self.plugin.updated = {
from = git.hash,
to = git_new.hash,
}
self.plugin.dirty = not vim.deep_equal(git, git_new)
end
end,
})
function Task:wait()
while self:is_running() do
vim.wait(10)
end
end