-
-
Notifications
You must be signed in to change notification settings - Fork 264
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Feature request] Support package snapshot #298 #370
Conversation
Resolve #298 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@c3n21 this looks great thanks for taking the time to implement this. I haven't tried it locally or anything just left some comments regarding some comments and a question or two.
Could you also maybe add some tests for this new functionality as well in tests
dir under snapshot_spec.lua
or something to validate that they are being created and read correctly. There are a few other tests there which should help guide you. Also there are docs on plenary
's repo
I should probably say that given this is whole new thing ultimately @wbthomason will have to review when he's free.
lua/packer.lua
Outdated
local f_snapshot, err = io.open(filename, 'r+') | ||
|
||
if err ~= nil then | ||
print(err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
probs a lingering debug print
lua/packer/install.lua
Outdated
@@ -32,6 +32,9 @@ local function install_plugin(plugin, display_win, results) | |||
end | |||
|
|||
local function do_install(_, plugins, missing_plugins, results) | |||
for index, value in ipairs(plugins) do | |||
print(fmt("index: %s, value: %s", index, value)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this intentional or left over from the initial implementation
lua/packer/snapshot.lua
Outdated
|
||
local function cfg(_config) config = _config end | ||
--[[ | ||
property: 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does this apply to? Is this an emmy lua annotation for something?
lua/packer.lua
Outdated
filename = util.join_paths(config.snapshot_path, filename) | ||
await(snapshot(filename, plugins)) | ||
await(a.main) | ||
--local delta = string.gsub(vim.fn.reltimestr(vim.fn.reltime(start_time)), ' ', '') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this left over from some profiling you were doing?
CHANGELOG.md
Outdated
@@ -0,0 +1,22 @@ | |||
# CHANGELOG |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will be one for @wbthomason to decide but packer hasn't previously maintained a changelog so not sure if we need/should be adding this here since it isn't particularly discoverable. I think this should probably just go in the main docs 🤷🏾
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually favor adding a changelog and linking to it from the main docs; the README is too long as it is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer to have changes in the changelog be date and commit-stamped, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To clarify: we also ought to document this feature in the main docs, but I like having a changelog too. Also, the docs should describe what a snapshot does/does not save.
Thank you for taking your time and review my PR, really appreciated. As soon as I have some free time I will clean up the mess I left in the code and create the tests. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CHANGELOG.md
Outdated
@@ -0,0 +1,22 @@ | |||
# CHANGELOG |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer to have changes in the changelog be date and commit-stamped, though.
CHANGELOG.md
Outdated
@@ -0,0 +1,22 @@ | |||
# CHANGELOG |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To clarify: we also ought to document this feature in the main docs, but I like having a changelog too. Also, the docs should describe what a snapshot does/does not save.
Just implemented PackerRollback.
|
@wbthomason @akinsho I've been working on the tests for the snapshot feature, but I can't get it to work with Could you give me some advice or point out where am I doing wrong? |
@c3n21 are you saying the tests don't run? or that you can't get them to pass? anyway re. running them you need to use Regarding the tests themselves from what I can see it appears you might be nesting describe blocks inside the tests themselves which won't work |
Oh and a side note about the tests can you call them just |
I've made removed the describe block inside the it block as you said and at least it runs now. However, if I add some async code the test gives me this result
but if I remove all the async code (remove await and |
@c3n21 the async testing has kind of been borrowed from plenary but packer has it's own async implementation so I think it's a little rough and obscure used together. When I've seen this error in the past it's usually meant that there is an error occurring in your test somewhere and this is how it is bubbling up. It could be the usage/syntax also I'll take a look and see if there are any incorrect usages |
@c3n21 I can't see any clear errors. One thing to keep in mind is that it's probably a good idea to test as close to your changes as possible for the time being i.e. just call your functions and assert they do the correct thing rather than trying to setup all of packer. A lot of those code paths are untested so you could easily get blocked by a completely separate issue that is unrelated to your change. |
The problem is that I need to be sure that at least one plugin is installed using Without that it's pointless... Atm I'm only checking if the snapshot have been created or not |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like snapshot
is only really dependent on list_missing_plugins
, so rather than trying to run install which I think will have quite a lot of side effects you could try to just mock that function. I think plenary has a few mock utilities but I don't know if those will work with a required module so another thing you could do is in the setup of your test you could replace require
with fake_require
and check if the value is that module and return a fake module
function test_require(module_name)
if module_name == "plugin_utils" then
return {list_missing_plugins = function() return {value_I_want} end}
end
return require(module_name)
end
-- Inside describe
_G.require = test_require
Sorry for the absence but I need to take some exams, so I'll keep working on this once I'll be done (by the end of the month) Just letting you know that I'm not dead and didn't give up about this feature :) |
Update: finally I've managed to pull it off. I have updated the first message of this thread so check it out for the "basic usage". I've also updated the documentation about the snapshot feature and added all the tests I could think of. There is a "bug" though I already documented in the first comment |
@wbthomason is this feature still needed? Because it still needs some improvement but if it's not needed I'm not going to improve this |
@c3n21 @.wbthomason is away atm and I haven't really been following things too closely. I don't think the need for this has changed though since it's driven entirely by all the people who would want to use it which I don't think has changed and no alternative has been implemented |
Ok, I'll mention you for the review when I'll be done |
@akinsho it should be good now. I've added the docs and updated the README.md. |
Hi and welcome back! For testing you could:
You can also specify in the config, e.g. |
Hi @wbthomason ! Did you manage to review this feature? |
I've updated my PR and now it seems to pass all the tests. |
Taking a look now. Thanks for the work @c3n21, guidance @shadmansaleh, and patience on everyone's parts. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks mostly good to go. However:
- We should resolve the currently failing tests
packer.snapshot.delete
does not seem to work for me - I get an error atsnapshot.lua:189
.- We should probably make a function analogous to
packer.snapshot
for snapshot deletion - i.e. at the top-level and with the async aspect wrapped appropriately.
@@ -794,6 +805,106 @@ packer.plugin_complete = function(lead, _, _) | |||
return completion_list | |||
end | |||
|
|||
---Snapshots installed plugins | |||
---@param snapshot_name string absolute path or just a snapshot name | |||
packer.snapshot = function(snapshot_name, ...) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick: I think we should either print an error or choose a sane default if snapshot_name
is nil
.
Right now I'm having some issues with the async code as mentioned here If anyone could help me figure out what's going out it would be great. |
@c3n21 I fixed the merge conflict and am re-running the tests; I'll look at the trace and see if I can figure out what's going on. |
@c3n21 Tests seem good to go now. Anything remaining before merge that you know of? |
I've made a minor refactor to make the code inside snapshot.create() shorter and a bit more modular. Thanks for your time! |
* when taking a snapshot, if `snapshot_name` exists the user will be asked if they want to overwrite the existing snapshot * better error handling * packer.snapshot() will default to '%Y-%m-%d' if no snapshot name is provided
* when taking a snapshot, if `snapshot_name` exists the user will be asked if they want to overwrite the existing snapshot * better error handling * packer.snapshot() will default to '%Y-%m-%d' if no snapshot name is provided
snapshot.delete is not async
@wbthomason I've polished a bit the code with the advices you gave me above. Also I think this might be useful because rollback hard resets the repo, and using it on the host machine could reset all the work not pushed, and running tests is as easy as running |
Thanks again, @c3n21! I think we're good to merge. |
Thanks @c3n21 - good to see this merged. Thanks for all your work on it. I've been using it since the .lua snapshot generation and have now switched to the more recent incarnation which has .json generation. I do notice that a side effect of async and vim.encode_json is that the .json file generated is not ordered or stable. This is not ideal for checking into source control and easily seeing what changed - I would expect this snapshot file to be source controlled by many people. I couldn't find a simple solution so I simply use 'jq --sort-keys' on the file afterwards - but if anyone knows a simple fix for packer to do it automatically I can raise a PR. |
The output isn't table because lua tables aren't stable . We can use list and sort the list before converting it to json. |
What about sorting the table before encoding? That should be consistent isn't it? EDIT: Just saw the reply |
I've solved keys sorting by additionally processing the final file, given that it's a json dump. echo $(jq --sort-keys . packages) > packages
prettier --parser json --write packages It's not hooked into vim but into my script for updating plugins with --headless mode. EDIT: now I see @smhc had already suggested that 🤦 |
Yes I use something like: packer.snapshot_format = function(fn)
local fullpath=vim.fn.stdpath('config')..'/'..fn
local tmp=vim.fn.stdpath('config')..'/temp_'..fn
os.execute('jq --sort-keys . '..fullpath..'> '..tmp)
os.rename(tmp, fullpath)
end The jq format seems as good as it can get, I don't think there's any need to run 'prettier'. I'd prefer to chain it with 'packer.snapshot' however I couldn't get the 'await', 'and_then' stuff working correctly, and without that there is race conditions causing empty files etc. So I just run it manually afterwards. |
It's just a first implementation and I only tested it with spec(1), namely:> spec can be a table with a function as its first element and config overrides as another element:
packer.startup({function() use 'tjdevries/colorbuddy.vim' end, config = { ... }})
## Usage### Make snapshotTo make a snapshot you just have to run:PackerSnapshot <snapshot name>
### Restore snapshot1. To restore a snapshot you have to add snapshot key in the config table you pass to packer.startup.So, if I want to restore a snapshot named foo I would have something like this:packer.startup(~~ ~~{function() use 'tjdevries/colorbuddy.vim' end, ~~ ~~config = { snapshot='foo' })
You can also specify the default directory in which retrieve and save snapshots with the following:packer.startup(~~ ~~{function() use 'tjdevries/colorbuddy.vim' end, ~~ ~~config = { snapshot='foo', snapshot_path='path/to/snapshots' })
By default the snapshot's location is in ~/.config/nvim/plugin2. Source the file:luafile %
3.:PackerUpdate
4. Restart NeoVim and enjoyIf you want to update you will need to remove snapshot from config table, and then source the file and update.Final Implementation
Usage
Make snapshot
To make a snapshot you just have to run
:PackerSnapshot <snapshot name>
; you can also append single plugins name to selectively snapshot only the plugins you want like:PackerSnapshot <snapshot name> <plugin_1> <plugin_2>
Restore snapshot
To rollback to a snapshot you have two ways:
snapshot name
which will be fetched inside config.snapshot_path (the default snapshot directory which is~/.cache/nvim/packer
but you can change it) or an absolute path.So, if I want to restore a snapshot named foo I would have something like this:
packer.startup( {function() use 'tjdevries/colorbuddy.vim' end, config = { snapshot='foo' })
In this way, every time you boot up NeoVim it will always load that snapshot.
:PackerRollback
which updates your installed plugins' commit to the snapshot you specified.After that you just need to run:PackerUpdate
and it will revert backDelete snapshot
PackerDelete <snapshot name>
, you can either provide only the snapshot name or an absolute path.Misc:
PackerRollback
andPackerDelete
can prompt which snapshots are available.vim.inspect
packer.rollback
hard resets the git repo of the given pluginKnown issues:
After running the update, the window will show error saying that you are indetached HEAD
mode