Skip to content

Commit

Permalink
feat: pin rocks to prevent updates (#278)
Browse files Browse the repository at this point in the history
* feat: pin rocks to prevent updates

* tests: add install/pin/update integration tests
  • Loading branch information
mrcjkb committed Apr 17, 2024
1 parent c54d6e2 commit 832c400
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 10 deletions.
19 changes: 19 additions & 0 deletions README.md
Expand Up @@ -347,6 +347,25 @@ Or, before rocks.nvim is initialised, with `require("rocks").packadd("<rock_name
> call to a heavy `setup` function,
> consider opening an issue on the plugin's issue tracker.
### Pinning installed plugins

You can pin plugins with the `pin` field, so that they are skipped
by `:Rocks update`.

For example:

```toml
[plugins.neorg]
version = "7.0.0"
pin = true
```

Or

```vim
:Rocks install neorg 7.0.0 pin=true
```

## :package: Extending `rocks.nvim`

This plugin provides a Lua API for extensibility.
Expand Down
27 changes: 19 additions & 8 deletions lua/rocks/operations/init.lua
Expand Up @@ -347,7 +347,8 @@ end

--- Attempts to update every available rock if it is not pinned.
--- This function invokes a UI.
operations.update = function()
---@param on_complete? function
operations.update = function(on_complete)
local progress_handle = progress.handle.create({
title = "Updating",
message = "Checking for updates...",
Expand Down Expand Up @@ -384,6 +385,10 @@ operations.update = function()

local ct = 0
for name, rock in pairs(outdated_rocks) do
local user_rock = user_rocks.plugins[rock.name] or user_rocks.rocks[rock.name]
if user_rock and user_rock.pin then
goto skip_update
end
nio.scheduler()
progress_handle:report({
message = name,
Expand All @@ -398,7 +403,6 @@ operations.update = function()
if success then
---@type rock_name
local rock_name = ret.name
local user_rock = user_rocks.plugins[rock_name]
if user_rock and user_rock.version then
-- Rock is configured as a table -> Update version.
user_rocks.plugins[rock_name].version = ret.version
Expand All @@ -417,6 +421,7 @@ operations.update = function()
percentage = get_percentage(ct, total_update_count),
})
end
::skip_update::
end
for _, handler in pairs(external_update_handlers) do
local function report_progress(message)
Expand Down Expand Up @@ -460,6 +465,9 @@ operations.update = function()
if config.generate_help_pages then
vim.cmd("helptags ALL")
end
if on_complete then
on_complete()
end
end)
end

Expand Down Expand Up @@ -588,17 +596,20 @@ operations.add = function(arg_list, callback)
if user_rock and user_rock.version then
-- Rock already exists in rock.toml and is configured as a table -> Update version.
user_rocks.plugins[rock_name].version = installed_rock.version
if install_spec.opt then
user_rocks.plugins[rock_name].opt = true
elseif user_rocks.plugins[rock_name].opt then
user_rocks.plugins[rock_name].opt = nil
for _, field in ipairs({ "opt", "pin" }) do
if install_spec[field] then
user_rocks.plugins[rock_name][field] = true
elseif user_rocks.plugins[rock_name][field] then
user_rocks.plugins[rock_name][field] = nil
end
end
elseif install_spec.opt then
elseif install_spec.opt or install_spec.pin then
-- toml-edit's metatable can't set a table directly.
-- Each field has to be set individually.
user_rocks.plugins[rock_name] = {}
user_rocks.plugins[rock_name].version = installed_rock.version
user_rocks.plugins[rock_name].opt = true
user_rocks.plugins[rock_name].opt = install_spec.opt
user_rocks.plugins[rock_name].pin = install_spec.pin
else
user_rocks.plugins[rock_name] = installed_rock.version
end
Expand Down
2 changes: 2 additions & 0 deletions lua/rocks/operations/parser.lua
Expand Up @@ -30,12 +30,14 @@ end

---@class rocks.InstallSpec
---@field opt? boolean If 'true', will not be loaded on startup. Can be loaded manually with `:Rocks[!] packadd`.
---@field pin? boolean If 'true', will not be updated.
---@field version? string version to install.

---@enum rocks.InstallSpecField
local InstallSpecField = {
version = tostring,
opt = str_to_bool,
pin = str_to_bool,
}

---@class rocks.ParseInstallArgsResult
Expand Down
33 changes: 33 additions & 0 deletions spec/operations/install_update_spec.lua
@@ -0,0 +1,33 @@
local tempdir = vim.fn.tempname()
vim.system({ "rm", "-r", tempdir }):wait()
vim.system({ "mkdir", "-p", tempdir }):wait()
vim.g.rocks_nvim = {
rocks_path = tempdir,
config_path = vim.fs.joinpath(tempdir, "rocks.toml"),
}
local nio = require("nio")
vim.env.PLENARY_TEST_TIMEOUT = 60000 * 5
describe("install/update", function()
local operations = require("rocks.operations")
local state = require("rocks.state")
nio.tests.it("install and update rocks", function()
local future = nio.control.future()
operations.add({ "neorg", "7.0.0" }, function()
future.set(true)
end)
future.wait()
local installed_rocks = state.installed_rocks()
assert.same({
name = "neorg",
version = "7.0.0",
}, installed_rocks.neorg)
future = nio.control.future()
operations.update(function()
future.set(true)
end)
future.wait()
installed_rocks = state.installed_rocks()
local updated_version = vim.version.parse(installed_rocks.neorg.version)
assert.True(updated_version > vim.version.parse("7.0.0"))
end)
end)
10 changes: 9 additions & 1 deletion spec/operations/parser_spec.lua
Expand Up @@ -19,14 +19,22 @@ describe("operations.parser", function()
assert.same({ "1.0.0", "foo" }, parser.parse_install_args({ "1.0.0", "opt=true", "foo" }).invalid_args)
end)

it("Non-boolean opt is invalid", function()
it("Parses boolean opt, non-boolean opt is invalid", function()
assert.same({ opt = true }, parser.parse_install_args({ "opt=true" }).spec)
assert.same({ opt = true }, parser.parse_install_args({ "opt=1" }).spec)
assert.same({ opt = false }, parser.parse_install_args({ "opt=false" }).spec)
assert.same({ opt = false }, parser.parse_install_args({ "opt=0" }).spec)
assert.same({ "opt=3" }, parser.parse_install_args({ "opt=3" }).invalid_args)
assert.same({ "opt=otherwise" }, parser.parse_install_args({ "opt=otherwise" }).invalid_args)
end)
it("Parses boolean and version range pins", function()
assert.same({ pin = true }, parser.parse_install_args({ "pin=true" }).spec)
assert.same({ pin = true }, parser.parse_install_args({ "pin=1" }).spec)
assert.same({ pin = false }, parser.parse_install_args({ "pin=false" }).spec)
assert.same({ pin = false }, parser.parse_install_args({ "pin=0" }).spec)
assert.same({ "pin=3" }, parser.parse_install_args({ "pin=3" }).invalid_args)
assert.same({ "pin=otherwise" }, parser.parse_install_args({ "pin=otherwise" }).invalid_args)
end)

it("Does not accept conflicting args", function()
assert.same(
Expand Down
35 changes: 35 additions & 0 deletions spec/operations/pin_spec.lua
@@ -0,0 +1,35 @@
local tempdir = vim.fn.tempname()
vim.system({ "rm", "-r", tempdir }):wait()
vim.system({ "mkdir", "-p", tempdir }):wait()
vim.g.rocks_nvim = {
rocks_path = tempdir,
config_path = vim.fs.joinpath(tempdir, "rocks.toml"),
}
local nio = require("nio")
vim.env.PLENARY_TEST_TIMEOUT = 60000 * 5
describe("install/pin/update", function()
local operations = require("rocks.operations")
local state = require("rocks.state")
nio.tests.it("update skips pinned install", function()
local future = nio.control.future()
operations.add({ "neorg", "7.0.0", "pin=true" }, function()
future.set(true)
end)
future.wait()
local installed_rocks = state.installed_rocks()
assert.same({
name = "neorg",
version = "7.0.0",
}, installed_rocks["neorg"])
future = nio.control.future()
operations.update(function()
future.set(true)
end)
future.wait()
installed_rocks = state.installed_rocks()
assert.same({
name = "neorg",
version = "7.0.0",
}, installed_rocks["neorg"])
end)
end)
2 changes: 1 addition & 1 deletion spec/operations/sync_spec.lua
@@ -1,4 +1,4 @@
local tempdir = vim.fs.dirname(vim.fn.tempname())
local tempdir = vim.fn.tempname()
vim.system({ "rm", "-r", tempdir }):wait()
vim.system({ "mkdir", "-p", tempdir }):wait()
vim.g.rocks_nvim = {
Expand Down

0 comments on commit 832c400

Please sign in to comment.