diff --git a/lua/glaze/runner.lua b/lua/glaze/runner.lua index 1d2ae10..11f672d 100644 --- a/lua/glaze/runner.lua +++ b/lua/glaze/runner.lua @@ -111,6 +111,25 @@ local function run_task(task) M._process_queue() end, }) + + if task.job_id <= 0 then + task.end_time = vim.uv.hrtime() + task.status = "error" + table.insert(task.output, "Failed to start command: " .. table.concat(cmd, " ")) + task.job_id = nil + + -- Call all registered callbacks with failure + if task.binary.callbacks then + vim.schedule(function() + for _, cb in pairs(task.binary.callbacks) do + cb(false) + end + end) + end + + M._notify() + vim.schedule(M._process_queue) + end end ---@private @@ -166,18 +185,12 @@ local function run(names, mode, opts) for _, name in ipairs(names) do local binary = glaze._binaries[name] - if binary then - if existing_names[name] then - -- Already queued or running, skip - elseif mode == "install" and glaze.is_installed(name) then - -- Skip already installed - else - table.insert(binaries, binary) - end - else + if not binary then if not opts.silent then vim.notify("Unknown binary: " .. name, vim.log.levels.WARN) end + elseif not existing_names[name] and not (mode == "install" and glaze.is_installed(name)) then + table.insert(binaries, binary) end end diff --git a/tests/run.lua b/tests/run.lua index 36ad265..ed1db52 100644 --- a/tests/run.lua +++ b/tests/run.lua @@ -107,6 +107,38 @@ test("register merges duplicate plugin registrations without duplicates", functi assert_eq(binary.plugins[2], "freeze.nvim") end) +test("runner marks failed job starts as errors", function() + local glaze = reset_glaze() + local runner = require("glaze.runner") + local original_jobstart = vim.fn.jobstart + + local callback_result = "unset" + glaze.setup({ auto_check = { enabled = false }, auto_install = { enabled = false } }) + glaze.register("fake-glaze-binary", "example.com/fake/glaze", { + plugin = "fake.nvim", + callback = function(success) + callback_result = success + end, + }) + glaze.is_installed = function() + return false + end + vim.fn.jobstart = function() + return -1 + end + + runner.install({ "fake-glaze-binary" }, { silent = true }) + vim.wait(100) + vim.fn.jobstart = original_jobstart + + local tasks = runner.tasks() + assert_eq(#tasks, 1, "failed job start should create one task") + assert_eq(tasks[1].status, "error", "failed job start should mark task as error") + assert_truthy(tasks[1].output[1]:match("Failed to start command"), "failed job start should keep output") + assert_eq(runner.is_running(), false, "failed job start should drain the runner") + assert_eq(callback_result, false, "failed job start should notify callbacks with false") +end) + for _, case in ipairs(results) do local ok, err = pcall(case.fn) if not ok then