aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHisham Muhammad <hisham@gobolinux.org>2019-10-18 06:35:36 -0300
committerGitHub <noreply@github.com>2019-10-18 06:35:36 -0300
commit3a3f2cbaa50d353f4c26b39f59c015a20f28c4ae (patch)
treeb39dd87b9180654f5724ca0cd624db0391ee23b6
parent879c6ef01f94df18a8457a3eb78aeafc4fe0a003 (diff)
downloadluarocks-3a3f2cbaa50d353f4c26b39f59c015a20f28c4ae.tar.gz
luarocks-3a3f2cbaa50d353f4c26b39f59c015a20f28c4ae.tar.bz2
luarocks-3a3f2cbaa50d353f4c26b39f59c015a20f28c4ae.zip
repos: rollback on installation failure (#1101)
-rw-r--r--spec/install_spec.lua35
-rw-r--r--src/luarocks/repos.lua68
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
280end 281end
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
304end
305
306local 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
294end 313end
295 314
296local function op_rename(op) 315local function op_rename(op)
@@ -311,6 +330,10 @@ local function op_rename(op)
311 end 330 end
312end 331end
313 332
333local function rollback_rename(op)
334 return op_rename({ src = op.dst, dst = op.src })
335end
336
314local function op_delete(op) 337local 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
323end 346end
324 347
348local function rollback_ops(ops, op_fn, n)
349 for i = 1, n do
350 op_fn(ops[i])
351 end
352end
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")