Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
92d8f8b9ee | |||
443a091e3e | |||
6259250120 | |||
25372aea36 | |||
30e7e05771
|
|||
d7a08ca820
|
|||
d141c762c1
|
|||
3d20ae8d2a
|
3
LICENSE
3
LICENSE
@ -1,6 +1,7 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2021 chiya.dev
|
(c) 2021 chiya.dev
|
||||||
|
(c) 2024 squibid
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
144
README.md
144
README.md
@ -1,14 +1,8 @@
|
|||||||
# dep
|
# dep
|
||||||
|
|
||||||
[](LICENSE)
|
|
||||||
[][4]
|
|
||||||
[][8]
|
|
||||||
[][9]
|
|
||||||
|
|
||||||
> This readme is a work in progress.
|
> This readme is a work in progress.
|
||||||
|
|
||||||
A versatile, declarative and correct [neovim][2] package manager in [Lua][3].
|
A versatile, declarative and correct [neovim][1] package manager in [Lua][2].
|
||||||
Originally written for personal use by [luaneko][4].
|
Originally written for personal use by [luaneko][3].
|
||||||
|
|
||||||
What does that mean?
|
What does that mean?
|
||||||
|
|
||||||
@ -16,20 +10,16 @@ What does that mean?
|
|||||||
2. `declarative` - packages are declared using simple Lua tables.
|
2. `declarative` - packages are declared using simple Lua tables.
|
||||||
3. `correct` - packages are always loaded in a correct and consistent order.
|
3. `correct` - packages are always loaded in a correct and consistent order.
|
||||||
|
|
||||||
See also luaneko's [neovim-configs][10] for an example of how dep can be used in practice.
|
See also squibid's [neovim-configs][5] for an example of how dep can be used in practice.
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
- [Neovim][1] 0.6+
|
||||||
- [Neovim][2] 0.6+
|
- [Git][4]
|
||||||
- [Git][5]
|
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
1. Create `lua/bootstrap.lua` in your neovim config directory.
|
1. Create `lua/bootstrap.lua` in your neovim config directory.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
-- ~/.config/nvim/lua/bootstrap.lua:
|
-- ~/.config/nvim/lua/bootstrap.lua:
|
||||||
-- automatically install `chiyadev/dep` on startup
|
-- automatically install `squibid/dep` on startup
|
||||||
local path = vim.fn.stdpath("data") .. "/site/pack/deps/opt/dep"
|
local path = vim.fn.stdpath("data") .. "/site/pack/deps/opt/dep"
|
||||||
|
|
||||||
if vim.fn.empty(vim.fn.glob(path)) > 0 then
|
if vim.fn.empty(vim.fn.glob(path)) > 0 then
|
||||||
@ -95,6 +85,10 @@ A package must be declared in the following format.
|
|||||||
-- Defaults to whatever the remote configured as their HEAD, which is usually "master".
|
-- Defaults to whatever the remote configured as their HEAD, which is usually "master".
|
||||||
branch = "develop",
|
branch = "develop",
|
||||||
|
|
||||||
|
-- [string] Overrides the commit ref to target
|
||||||
|
-- Defaults to the latest commit on the current branch
|
||||||
|
commit = "e76cb03",
|
||||||
|
|
||||||
-- [boolean] Prevents the package from being loaded.
|
-- [boolean] Prevents the package from being loaded.
|
||||||
disable = true,
|
disable = true,
|
||||||
|
|
||||||
@ -287,97 +281,6 @@ require "dep" {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Separating code into modules
|
|
||||||
|
|
||||||
Suppose you split your `init.lua` into two files `packages/search.lua` and
|
|
||||||
`packages/vcs.lua`, which declare the packages [telescope.nvim][6] and [vim-fugitive][7] respectively.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
-- ~/.config/nvim/lua/packages/search.lua:
|
|
||||||
return {
|
|
||||||
{
|
|
||||||
"nvim-telescope/telescope.nvim",
|
|
||||||
requires = "nvim-lua/plenary.nvim"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```lua
|
|
||||||
-- ~/.config/nvim/lua/packages/vcs.lua:
|
|
||||||
return {
|
|
||||||
"tpope/vim-fugitive"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Package specifications from other modules can be loaded using the `modules` option.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
require "dep" {
|
|
||||||
modules = {
|
|
||||||
prefix = "packages.",
|
|
||||||
"search",
|
|
||||||
"vcs"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
-- the above is equivalent to
|
|
||||||
require "dep" {
|
|
||||||
modules = {
|
|
||||||
"packages.search",
|
|
||||||
"packages.vcs"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
-- which is equivalent to
|
|
||||||
local packages = {}
|
|
||||||
|
|
||||||
for _, package in ipairs(require "packages.search") do
|
|
||||||
table.insert(packages, package)
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, package in ipairs(require "packages.vcs") do
|
|
||||||
table.insert(packages, package)
|
|
||||||
end
|
|
||||||
|
|
||||||
require("dep")(packages)
|
|
||||||
|
|
||||||
-- which is ultimately equivalent to
|
|
||||||
require "dep" {
|
|
||||||
{
|
|
||||||
"nvim-telescope/telescope.nvim",
|
|
||||||
requires = "nvim-lua/plenary.nvim"
|
|
||||||
},
|
|
||||||
"tpope/vim-fugitive"
|
|
||||||
}
|
|
||||||
|
|
||||||
-- all of the above are guaranteed to load plenary.nvim before telescope.nvim.
|
|
||||||
-- order of telescope.nvim and vim-fugitive is consistent but unspecified.
|
|
||||||
```
|
|
||||||
|
|
||||||
Entire modules can be marked as disabled, which disables all top-level packages declared in that module.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
return {
|
|
||||||
disable = true,
|
|
||||||
{
|
|
||||||
"user/package",
|
|
||||||
disabled = true, -- implied by module
|
|
||||||
requires = {
|
|
||||||
{
|
|
||||||
"user/dependency",
|
|
||||||
-- disabled = true -- not implied
|
|
||||||
}
|
|
||||||
},
|
|
||||||
deps = {
|
|
||||||
{
|
|
||||||
"user/dependent",
|
|
||||||
disabled = true -- implied by dependency
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Miscellaneous configuration
|
## Miscellaneous configuration
|
||||||
|
|
||||||
dep accepts configuration parameters as named fields in the package list.
|
dep accepts configuration parameters as named fields in the package list.
|
||||||
@ -390,14 +293,10 @@ require "dep" {
|
|||||||
-- "always": synchronize all packages on startup
|
-- "always": synchronize all packages on startup
|
||||||
sync = "new",
|
sync = "new",
|
||||||
|
|
||||||
-- [array] Specifies the modules to load package specifications from.
|
-- [function] Callback when dep is (re)loaded
|
||||||
-- Defaults to an empty table.
|
-- if a table is returned it will be read as a table of config specs
|
||||||
-- Items can be either an array of package specifications,
|
load = function()
|
||||||
-- or a string that indicates the name of the module from which the array of package specifications is loaded.
|
end
|
||||||
modules = {
|
|
||||||
-- [string] Prefix string to prepend to all module names.
|
|
||||||
prefix = "",
|
|
||||||
},
|
|
||||||
|
|
||||||
-- list of package specs...
|
-- list of package specs...
|
||||||
}
|
}
|
||||||
@ -407,13 +306,8 @@ require "dep" {
|
|||||||
|
|
||||||
dep is licensed under the [MIT License](LICENSE).
|
dep is licensed under the [MIT License](LICENSE).
|
||||||
|
|
||||||
[1]: https://chiya.dev/posts/2021-11-27-why-package-manager
|
[1]: https://neovim.io/
|
||||||
[2]: https://neovim.io/
|
[2]: https://www.lua.org/
|
||||||
[3]: https://www.lua.org/
|
[3]: https://github.com/luaneko
|
||||||
[4]: https://github.com/luaneko
|
[4]: https://git-scm.com/
|
||||||
[5]: https://git-scm.com/
|
[5]: https://git.squi.bid/nvim
|
||||||
[6]: https://github.com/nvim-telescope/telescope.nvim
|
|
||||||
[7]: https://github.com/tpope/vim-fugitive
|
|
||||||
[8]: https://GitHub.com/chiyadev/dep/issues
|
|
||||||
[9]: https://github.com/chiyadev/dep/graphs/contributors
|
|
||||||
[10]: https://github.com/luaneko/neovim-config
|
|
||||||
|
135
lua/dep.lua
135
lua/dep.lua
@ -11,7 +11,7 @@ local logger = require("dep.log").global
|
|||||||
local proc = require("dep.proc")
|
local proc = require("dep.proc")
|
||||||
|
|
||||||
local initialized, perf, config_path, base_dir
|
local initialized, perf, config_path, base_dir
|
||||||
local packages, root
|
local packages, root, load
|
||||||
|
|
||||||
local function bench(name, code, ...)
|
local function bench(name, code, ...)
|
||||||
local start = os.clock()
|
local start = os.clock()
|
||||||
@ -78,6 +78,7 @@ local function register(spec, overrides)
|
|||||||
package.url = spec.url or package.url or ("https://github.com/" .. id .. ".git")
|
package.url = spec.url or package.url or ("https://github.com/" .. id .. ".git")
|
||||||
package.branch = spec.branch or package.branch
|
package.branch = spec.branch or package.branch
|
||||||
package.dir = base_dir .. package.name
|
package.dir = base_dir .. package.name
|
||||||
|
package.commit = spec.commit
|
||||||
package.pin = overrides.pin or spec.pin or package.pin
|
package.pin = overrides.pin or spec.pin or package.pin
|
||||||
package.enabled = not overrides.disable and not spec.disable and package.enabled
|
package.enabled = not overrides.disable and not spec.disable and package.enabled
|
||||||
|
|
||||||
@ -127,27 +128,6 @@ local function register_recursive(list, overrides)
|
|||||||
error(string.format("%s (spec=%s)", err, vim.inspect(list[i])))
|
error(string.format("%s (spec=%s)", err, vim.inspect(list[i])))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if list.modules then
|
|
||||||
for i = 1, #list.modules do
|
|
||||||
local name, module = "<unnamed module>", list.modules[i]
|
|
||||||
|
|
||||||
if type(module) == "string" then
|
|
||||||
if list.modules.prefix then
|
|
||||||
module = list.modules.prefix .. module
|
|
||||||
end
|
|
||||||
|
|
||||||
name, module = module, require(module)
|
|
||||||
end
|
|
||||||
|
|
||||||
name = module.name or name
|
|
||||||
|
|
||||||
local ok, err = pcall(register_recursive, module, overrides)
|
|
||||||
if not ok then
|
|
||||||
error(string.format("%s <- %s", err, name))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function sort_dependencies()
|
local function sort_dependencies()
|
||||||
@ -417,6 +397,16 @@ local function reload()
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function reload_all()
|
local function reload_all()
|
||||||
|
-- recall the load function
|
||||||
|
if load and type(load) == "function" then
|
||||||
|
local ok, ret = pcall(load)
|
||||||
|
if ok and type(ret) == "table" then
|
||||||
|
register_recursive(ret)
|
||||||
|
else
|
||||||
|
logger:log("error", ret)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
for i = 1, #packages do
|
for i = 1, #packages do
|
||||||
local package = packages[i]
|
local package = packages[i]
|
||||||
package.loaded, package.subtree_loaded = false, false
|
package.loaded, package.subtree_loaded = false, false
|
||||||
@ -504,33 +494,55 @@ local function sync(package, cb)
|
|||||||
log_err(before)
|
log_err(before)
|
||||||
cb(err)
|
cb(err)
|
||||||
else
|
else
|
||||||
proc.git_fetch(package.dir, "origin", package.branch or "HEAD", function(err, message)
|
if package.commit then
|
||||||
if err then
|
proc.git_checkout(package.dir, package.branch, package.commit, function(err, message)
|
||||||
log_err(message)
|
if err then
|
||||||
cb(err)
|
log_err(message)
|
||||||
else
|
cb(err)
|
||||||
proc.git_rev_parse(package.dir, "FETCH_HEAD", function(err, after)
|
else
|
||||||
if err then
|
proc.git_rev_parse(package.dir, package.commit, function(err, after)
|
||||||
log_err(after)
|
if err then
|
||||||
cb(err)
|
log_err(after)
|
||||||
elseif before == after then
|
|
||||||
logger:log("skip", string.format("skipped %s", package.id))
|
|
||||||
cb(err)
|
|
||||||
else
|
|
||||||
proc.git_reset(package.dir, after, function(err, message)
|
|
||||||
if err then
|
|
||||||
log_err(message)
|
|
||||||
else
|
|
||||||
mark_reconfigure(package)
|
|
||||||
logger:log("update", string.format("updated %s; %s -> %s", package.id, before, after))
|
|
||||||
end
|
|
||||||
|
|
||||||
cb(err)
|
cb(err)
|
||||||
end)
|
elseif before == after then
|
||||||
end
|
logger:log("skip", string.format("skipped %s", package.id))
|
||||||
end)
|
cb(err)
|
||||||
end
|
else
|
||||||
end)
|
mark_reconfigure(package)
|
||||||
|
logger:log("update", string.format("updated %s; %s -> %s", package.id, before, after))
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
else
|
||||||
|
proc.git_fetch(package.dir, "origin", package.branch or "HEAD", function(err, message)
|
||||||
|
if err then
|
||||||
|
log_err(message)
|
||||||
|
cb(err)
|
||||||
|
else
|
||||||
|
proc.git_rev_parse(package.dir, "FETCH_HEAD", function(err, after)
|
||||||
|
if err then
|
||||||
|
log_err(after)
|
||||||
|
cb(err)
|
||||||
|
elseif before == after then
|
||||||
|
logger:log("skip", string.format("skipped %s", package.id))
|
||||||
|
cb(err)
|
||||||
|
else
|
||||||
|
proc.git_reset(package.dir, after, function(err, message)
|
||||||
|
if err then
|
||||||
|
log_err(message)
|
||||||
|
else
|
||||||
|
mark_reconfigure(package)
|
||||||
|
logger:log("update", string.format("updated %s; %s -> %s", package.id, before, after))
|
||||||
|
end
|
||||||
|
|
||||||
|
cb(err)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
else
|
else
|
||||||
@ -538,9 +550,21 @@ local function sync(package, cb)
|
|||||||
if err then
|
if err then
|
||||||
logger:log("error", string.format("failed to install %s; reason: %s", package.id, message))
|
logger:log("error", string.format("failed to install %s; reason: %s", package.id, message))
|
||||||
else
|
else
|
||||||
package.exists = true
|
if package.commit then
|
||||||
mark_reconfigure(package)
|
proc.git_checkout(package.dir, package.branch, package.commit, function(err, message)
|
||||||
logger:log("install", string.format("installed %s", package.id))
|
if err then
|
||||||
|
logger:log("error", string.format("failed to checkout %s; reason: %s", package.id, message))
|
||||||
|
else
|
||||||
|
package.exists = true
|
||||||
|
mark_reconfigure(package)
|
||||||
|
logger:log("install", string.format("installed %s", package.id))
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
else
|
||||||
|
package.exists = true
|
||||||
|
mark_reconfigure(package)
|
||||||
|
logger:log("install", string.format("installed %s", package.id))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
cb(err)
|
cb(err)
|
||||||
@ -850,6 +874,15 @@ return setmetatable({
|
|||||||
|
|
||||||
bench("load", function()
|
bench("load", function()
|
||||||
root = register("squibid/dep")
|
root = register("squibid/dep")
|
||||||
|
if config["load"] and type(config["load"]) == "function" then
|
||||||
|
local ok, ret = pcall(config["load"])
|
||||||
|
if ok and type(ret) == "table" then
|
||||||
|
load = config["load"]
|
||||||
|
register_recursive(ret)
|
||||||
|
else
|
||||||
|
logger:log("error", ret)
|
||||||
|
end
|
||||||
|
end
|
||||||
register_recursive(config)
|
register_recursive(config)
|
||||||
sort_dependencies()
|
sort_dependencies()
|
||||||
ensure_acyclic()
|
ensure_acyclic()
|
||||||
|
@ -168,7 +168,6 @@ local PackageStore = setmetatable({
|
|||||||
assert(type(specs) == "table", "package list must be a table")
|
assert(type(specs) == "table", "package list must be a table")
|
||||||
assert(specs.pin == nil or type(specs.pin) == "boolean", "package list pin must be a boolean")
|
assert(specs.pin == nil or type(specs.pin) == "boolean", "package list pin must be a boolean")
|
||||||
assert(specs.disable == nil or type(specs.disable) == "boolean", "package list disable must be a boolean")
|
assert(specs.disable == nil or type(specs.disable) == "boolean", "package list disable must be a boolean")
|
||||||
assert(specs.modules == nil or type(specs.modules) == "table", "package list module list must be a table")
|
|
||||||
|
|
||||||
scope = scope or {}
|
scope = scope or {}
|
||||||
scope = {
|
scope = {
|
||||||
@ -181,20 +180,6 @@ local PackageStore = setmetatable({
|
|||||||
for i = 1, #specs do
|
for i = 1, #specs do
|
||||||
self:add_spec(specs[i], scope)
|
self:add_spec(specs[i], scope)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- recursively add referenced spec list modules
|
|
||||||
if specs.modules then
|
|
||||||
local prefix = specs.modules.prefix or ""
|
|
||||||
for i = 1, #specs.modules do
|
|
||||||
local name = specs.modules[i]
|
|
||||||
assert(type(name) == "string", "package list inner module name must be a string")
|
|
||||||
name = prefix .. name
|
|
||||||
|
|
||||||
local module = require(name)
|
|
||||||
assert(type(module) == "table", "package list inner module did not return a spec list table")
|
|
||||||
self:add_specs(module, scope)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
end,
|
||||||
|
|
||||||
--- Ensures there are no circular dependencies in this package store.
|
--- Ensures there are no circular dependencies in this package store.
|
||||||
|
@ -61,4 +61,14 @@ function proc.git_reset(dir, treeish, cb)
|
|||||||
proc.exec("git", args, dir, git_env, cb)
|
proc.exec("git", args, dir, git_env, cb)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function proc.git_checkout(dir, branch, commit, cb)
|
||||||
|
local args = { "fetch", "--depth=2147483647", "origin", branch }
|
||||||
|
proc.exec("git", args, dir, git_env, function(err, message)
|
||||||
|
cb(err, message)
|
||||||
|
|
||||||
|
args = { "checkout", commit }
|
||||||
|
proc.exec("git", args, dir, git_env, cb)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
return proc
|
return proc
|
||||||
|
Reference in New Issue
Block a user