diff options
author | Hisham Muhammad <hisham@gobolinux.org> | 2019-10-18 06:35:36 -0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-10-18 06:35:36 -0300 |
commit | 3a3f2cbaa50d353f4c26b39f59c015a20f28c4ae (patch) | |
tree | b39dd87b9180654f5724ca0cd624db0391ee23b6 | |
parent | 879c6ef01f94df18a8457a3eb78aeafc4fe0a003 (diff) | |
download | luarocks-3a3f2cbaa50d353f4c26b39f59c015a20f28c4ae.tar.gz luarocks-3a3f2cbaa50d353f4c26b39f59c015a20f28c4ae.tar.bz2 luarocks-3a3f2cbaa50d353f4c26b39f59c015a20f28c4ae.zip |
repos: rollback on installation failure (#1101)
-rw-r--r-- | spec/install_spec.lua | 35 | ||||
-rw-r--r-- | src/luarocks/repos.lua | 68 |
2 files changed, 88 insertions, 15 deletions
diff --git a/spec/install_spec.lua b/spec/install_spec.lua index 3b0b22fb..9b082ab8 100644 --- a/spec/install_spec.lua +++ b/spec/install_spec.lua | |||
@@ -81,7 +81,7 @@ describe("luarocks install #integration", function() | |||
81 | assert.is_true(run.luarocks_bool("show luasocket")) | 81 | assert.is_true(run.luarocks_bool("show luasocket")) |
82 | end) | 82 | end) |
83 | 83 | ||
84 | it("installs a package without its documentation #only", function() | 84 | it("installs a package without its documentation", function() |
85 | assert.is_true(run.luarocks_bool("install wsapi 1.6 --no-doc")) | 85 | assert.is_true(run.luarocks_bool("install wsapi 1.6 --no-doc")) |
86 | assert.is_true(run.luarocks_bool("show wsapi 1.6")) | 86 | assert.is_true(run.luarocks_bool("show wsapi 1.6")) |
87 | assert.is.falsy(lfs.attributes(testing_paths.testing_sys_rocks .. "/wsapi/1.6-1/doc")) | 87 | assert.is.falsy(lfs.attributes(testing_paths.testing_sys_rocks .. "/wsapi/1.6-1/doc")) |
@@ -195,6 +195,39 @@ describe("luarocks install #integration", function() | |||
195 | assert.is_true(os.remove("luasocket-3.0rc1-2." .. test_env.platform .. ".rock")) | 195 | assert.is_true(os.remove("luasocket-3.0rc1-2." .. test_env.platform .. ".rock")) |
196 | end) | 196 | end) |
197 | 197 | ||
198 | it("installation rolls back on failure", function() | ||
199 | assert.is_true(run.luarocks_bool("build --pack-binary-rock luasocket 3.0rc1-2")) | ||
200 | local luadir = testing_paths.testing_sys_tree .. "/share/lua/"..env_variables.LUA_VERSION | ||
201 | lfs.mkdir(luadir) | ||
202 | |||
203 | run.luarocks_bool("remove " .. "luasocket") | ||
204 | |||
205 | -- create a file where a folder should be | ||
206 | local fd = io.open(luadir .. "/socket", "w") | ||
207 | fd:write("\n") | ||
208 | fd:close() | ||
209 | |||
210 | -- try to install and fail | ||
211 | assert.is_false(run.luarocks_bool("install " .. "luasocket-3.0rc1-2." .. test_env.platform .. ".rock")) | ||
212 | |||
213 | -- file is still there | ||
214 | assert.is.truthy(lfs.attributes(luadir .. "/socket")) | ||
215 | -- no left overs from failed installation | ||
216 | assert.is.falsy(lfs.attributes(luadir .. "/mime.lua")) | ||
217 | |||
218 | -- remove file | ||
219 | assert.is_true(os.remove(luadir .. "/socket")) | ||
220 | |||
221 | -- try again and succeed | ||
222 | assert.is_true(run.luarocks_bool("install " .. "luasocket-3.0rc1-2." .. test_env.platform .. ".rock")) | ||
223 | |||
224 | -- files installed successfully | ||
225 | assert.is.truthy(lfs.attributes(luadir .. "/socket/ftp.lua")) | ||
226 | assert.is.truthy(lfs.attributes(luadir .. "/mime.lua")) | ||
227 | |||
228 | assert.is_true(os.remove("luasocket-3.0rc1-2." .. test_env.platform .. ".rock")) | ||
229 | end) | ||
230 | |||
198 | it("binary rock of cprint", function() | 231 | it("binary rock of cprint", function() |
199 | assert.is_true(run.luarocks_bool("build --pack-binary-rock cprint")) | 232 | assert.is_true(run.luarocks_bool("build --pack-binary-rock cprint")) |
200 | assert.is_true(run.luarocks_bool("install cprint-0.1-2." .. test_env.platform .. ".rock")) | 233 | assert.is_true(run.luarocks_bool("install cprint-0.1-2." .. test_env.platform .. ".rock")) |
diff --git a/src/luarocks/repos.lua b/src/luarocks/repos.lua index 0058d928..2b0d3fcb 100644 --- a/src/luarocks/repos.lua +++ b/src/luarocks/repos.lua | |||
@@ -276,6 +276,7 @@ local function backup_existing(should_backup, target) | |||
276 | if not move_ok then | 276 | if not move_ok then |
277 | return nil, move_err | 277 | return nil, move_err |
278 | end | 278 | end |
279 | return backup | ||
279 | end | 280 | end |
280 | end | 281 | end |
281 | 282 | ||
@@ -285,12 +286,30 @@ local function op_install(op) | |||
285 | return nil, err | 286 | return nil, err |
286 | end | 287 | end |
287 | 288 | ||
289 | local backup, err = backup_existing(op.backup, op.realdst or op.dst) | ||
290 | if err then | ||
291 | return nil, err | ||
292 | end | ||
293 | if backup then | ||
294 | op.backup_file = backup | ||
295 | end | ||
296 | |||
288 | ok, err = op.fn(op.src, op.dst, op.backup) | 297 | ok, err = op.fn(op.src, op.dst, op.backup) |
289 | if not ok then | 298 | if not ok then |
290 | return nil, err | 299 | return nil, err |
291 | end | 300 | end |
292 | 301 | ||
293 | fs.remove_dir_tree_if_empty(dir.dir_name(op.src)) | 302 | fs.remove_dir_tree_if_empty(dir.dir_name(op.src)) |
303 | return true | ||
304 | end | ||
305 | |||
306 | local function rollback_install(op) | ||
307 | fs.delete(op.dst) | ||
308 | if op.backup_file then | ||
309 | fs.move(op.backup_file, op.dst) | ||
310 | end | ||
311 | fs.remove_dir_tree_if_empty(dir.dir_name(op.dst)) | ||
312 | return true | ||
294 | end | 313 | end |
295 | 314 | ||
296 | local function op_rename(op) | 315 | local function op_rename(op) |
@@ -311,6 +330,10 @@ local function op_rename(op) | |||
311 | end | 330 | end |
312 | end | 331 | end |
313 | 332 | ||
333 | local function rollback_rename(op) | ||
334 | return op_rename({ src = op.dst, dst = op.src }) | ||
335 | end | ||
336 | |||
314 | local function op_delete(op) | 337 | local function op_delete(op) |
315 | if op.suffix then | 338 | if op.suffix then |
316 | local suffix = check_suffix(op.name, op.suffix) | 339 | local suffix = check_suffix(op.name, op.suffix) |
@@ -322,6 +345,12 @@ local function op_delete(op) | |||
322 | return ok, err | 345 | return ok, err |
323 | end | 346 | end |
324 | 347 | ||
348 | local function rollback_ops(ops, op_fn, n) | ||
349 | for i = 1, n do | ||
350 | op_fn(ops[i]) | ||
351 | end | ||
352 | end | ||
353 | |||
325 | --- Deploy a package from the rocks subdirectory. | 354 | --- Deploy a package from the rocks subdirectory. |
326 | -- @param name string: name of package | 355 | -- @param name string: name of package |
327 | -- @param version string: exact package version in string format | 356 | -- @param version string: exact package version in string format |
@@ -341,23 +370,19 @@ function repos.deploy_files(name, version, wrap_bin_scripts, deps_mode) | |||
341 | local renames = {} | 370 | local renames = {} |
342 | local installs = {} | 371 | local installs = {} |
343 | 372 | ||
344 | local function install_binary(source, target, should_backup) | 373 | local function install_binary(source, target) |
345 | if wrap_bin_scripts and fs.is_lua(source) then | 374 | if wrap_bin_scripts and fs.is_lua(source) then |
346 | backup_existing(should_backup, target .. (cfg.wrapper_suffix or "")) | ||
347 | return fs.wrap_script(source, target, deps_mode, name, version) | 375 | return fs.wrap_script(source, target, deps_mode, name, version) |
348 | else | 376 | else |
349 | backup_existing(should_backup, target) | ||
350 | return fs.copy_binary(source, target) | 377 | return fs.copy_binary(source, target) |
351 | end | 378 | end |
352 | end | 379 | end |
353 | 380 | ||
354 | local function move_lua(source, target, should_backup) | 381 | local function move_lua(source, target) |
355 | backup_existing(should_backup, target) | ||
356 | return fs.move(source, target, "read") | 382 | return fs.move(source, target, "read") |
357 | end | 383 | end |
358 | 384 | ||
359 | local function move_lib(source, target, should_backup) | 385 | local function move_lib(source, target) |
360 | backup_existing(should_backup, target) | ||
361 | return fs.move(source, target, "exec") | 386 | return fs.move(source, target, "exec") |
362 | end | 387 | end |
363 | 388 | ||
@@ -372,8 +397,12 @@ function repos.deploy_files(name, version, wrap_bin_scripts, deps_mode) | |||
372 | local cur_paths = get_deploy_paths(cur_name, cur_version, "bin", file_path, repo) | 397 | local cur_paths = get_deploy_paths(cur_name, cur_version, "bin", file_path, repo) |
373 | table.insert(renames, { src = cur_paths.nv, dst = cur_paths.v, suffix = cfg.wrapper_suffix }) | 398 | table.insert(renames, { src = cur_paths.nv, dst = cur_paths.v, suffix = cfg.wrapper_suffix }) |
374 | end | 399 | end |
400 | local target = mode == "nv" and paths.nv or paths.v | ||
375 | local backup = name ~= cur_name or version ~= cur_version | 401 | local backup = name ~= cur_name or version ~= cur_version |
376 | table.insert(installs, { fn = install_binary, src = source, dst = mode == "nv" and paths.nv or paths.v, backup = backup }) | 402 | local realdst = (wrap_bin_scripts and fs.is_lua(source)) |
403 | and (target .. (cfg.wrapper_suffix or "")) | ||
404 | or target | ||
405 | table.insert(installs, { fn = install_binary, src = source, dst = target, backup = backup, realdst = realdst }) | ||
377 | end) | 406 | end) |
378 | end | 407 | end |
379 | 408 | ||
@@ -390,8 +419,9 @@ function repos.deploy_files(name, version, wrap_bin_scripts, deps_mode) | |||
390 | cur_paths = get_deploy_paths(cur_name, cur_version, "lib", file_path:gsub("%.lua$", "." .. cfg.lib_extension), repo) | 419 | cur_paths = get_deploy_paths(cur_name, cur_version, "lib", file_path:gsub("%.lua$", "." .. cfg.lib_extension), repo) |
391 | table.insert(renames, { src = cur_paths.nv, dst = cur_paths.v }) | 420 | table.insert(renames, { src = cur_paths.nv, dst = cur_paths.v }) |
392 | end | 421 | end |
422 | local target = mode == "nv" and paths.nv or paths.v | ||
393 | local backup = name ~= cur_name or version ~= cur_version | 423 | local backup = name ~= cur_name or version ~= cur_version |
394 | table.insert(installs, { fn = move_lua, src = source, dst = mode == "nv" and paths.nv or paths.v, backup = backup }) | 424 | table.insert(installs, { fn = move_lua, src = source, dst = target, backup = backup }) |
395 | end) | 425 | end) |
396 | end | 426 | end |
397 | 427 | ||
@@ -408,16 +438,26 @@ function repos.deploy_files(name, version, wrap_bin_scripts, deps_mode) | |||
408 | cur_paths = get_deploy_paths(cur_name, cur_version, "lib", file_path, repo) | 438 | cur_paths = get_deploy_paths(cur_name, cur_version, "lib", file_path, repo) |
409 | table.insert(renames, { src = cur_paths.nv, dst = cur_paths.v }) | 439 | table.insert(renames, { src = cur_paths.nv, dst = cur_paths.v }) |
410 | end | 440 | end |
441 | local target = mode == "nv" and paths.nv or paths.v | ||
411 | local backup = name ~= cur_name or version ~= cur_version | 442 | local backup = name ~= cur_name or version ~= cur_version |
412 | table.insert(installs, { fn = move_lib, src = source, dst = mode == "nv" and paths.nv or paths.v, backup = backup }) | 443 | table.insert(installs, { fn = move_lib, src = source, dst = target, backup = backup }) |
413 | end) | 444 | end) |
414 | end | 445 | end |
415 | 446 | ||
416 | for _, op in ipairs(renames) do | 447 | for i, op in ipairs(renames) do |
417 | op_rename(op) | 448 | local ok, err = op_rename(op) |
449 | if not ok then | ||
450 | rollback_ops(renames, rollback_rename, i - 1) | ||
451 | return nil, err | ||
452 | end | ||
418 | end | 453 | end |
419 | for _, op in ipairs(installs) do | 454 | for i, op in ipairs(installs) do |
420 | op_install(op) | 455 | local ok, err = op_install(op) |
456 | if not ok then | ||
457 | rollback_ops(installs, rollback_install, i - 1) | ||
458 | rollback_ops(renames, rollback_rename, #renames) | ||
459 | return nil, err | ||
460 | end | ||
421 | end | 461 | end |
422 | 462 | ||
423 | local writer = require("luarocks.manif.writer") | 463 | local writer = require("luarocks.manif.writer") |