Add ability to rename environments (#43296)

This commit is contained in:
Kyle Knoepfel 2024-03-28 16:15:04 -05:00 committed by GitHub
parent 9f2451ddff
commit 5f9228746e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 239 additions and 5 deletions

View file

@ -9,6 +9,7 @@
import shutil import shutil
import sys import sys
import tempfile import tempfile
from pathlib import Path
from typing import Optional from typing import Optional
import llnl.string as string import llnl.string as string
@ -44,6 +45,7 @@
"deactivate", "deactivate",
"create", "create",
["remove", "rm"], ["remove", "rm"],
["rename", "mv"],
["list", "ls"], ["list", "ls"],
["status", "st"], ["status", "st"],
"loads", "loads",
@ -472,11 +474,82 @@ def env_remove(args):
tty.msg(f"Successfully removed environment '{bad_env_name}'") tty.msg(f"Successfully removed environment '{bad_env_name}'")
#
# env rename
#
def env_rename_setup_parser(subparser):
"""rename an existing environment"""
subparser.add_argument(
"mv_from", metavar="from", help="name (or path) of existing environment"
)
subparser.add_argument(
"mv_to", metavar="to", help="new name (or path) for existing environment"
)
subparser.add_argument(
"-d",
"--dir",
action="store_true",
help="the specified arguments correspond to directory paths",
)
subparser.add_argument(
"-f", "--force", action="store_true", help="allow overwriting of an existing environment"
)
def env_rename(args):
"""Rename an environment.
This renames a managed environment or moves an anonymous environment.
"""
# Directory option has been specified
if args.dir:
if not ev.is_env_dir(args.mv_from):
tty.die("The specified path does not correspond to a valid spack environment")
from_path = Path(args.mv_from)
if not args.force:
if ev.is_env_dir(args.mv_to):
tty.die(
"The new path corresponds to an existing environment;"
" specify the --force flag to overwrite it."
)
if Path(args.mv_to).exists():
tty.die("The new path already exists; specify the --force flag to overwrite it.")
to_path = Path(args.mv_to)
# Name option being used
elif ev.exists(args.mv_from):
from_path = ev.environment.environment_dir_from_name(args.mv_from)
if not args.force and ev.exists(args.mv_to):
tty.die(
"The new name corresponds to an existing environment;"
" specify the --force flag to overwrite it."
)
to_path = ev.environment.root(args.mv_to)
# Neither
else:
tty.die("The specified name does not correspond to a managed spack environment")
# Guard against renaming from or to an active environment
active_env = ev.active_environment()
if active_env:
from_env = ev.Environment(from_path)
if from_env.path == active_env.path:
tty.die("Cannot rename active environment")
if to_path == active_env.path:
tty.die(f"{args.mv_to} is an active environment")
shutil.rmtree(to_path, ignore_errors=True)
fs.rename(from_path, to_path)
tty.msg(f"Successfully renamed environment {args.mv_from} to {args.mv_to}")
# #
# env list # env list
# #
def env_list_setup_parser(subparser): def env_list_setup_parser(subparser):
"""list available environments""" """list managed environments"""
def env_list(args): def env_list(args):

View file

@ -188,6 +188,127 @@ def test_env_remove(capfd):
assert "bar" not in out assert "bar" not in out
def test_env_rename_managed(capfd):
# Need real environment
with pytest.raises(spack.main.SpackCommandError):
env("rename", "foo", "bar")
assert (
"The specified name does not correspond to a managed spack environment"
in capfd.readouterr()[0]
)
env("create", "foo")
out = env("list")
assert "foo" in out
out = env("rename", "foo", "bar")
assert "Successfully renamed environment foo to bar" in out
out = env("list")
assert "foo" not in out
assert "bar" in out
bar = ev.read("bar")
with bar:
# Cannot rename active environment
with pytest.raises(spack.main.SpackCommandError):
env("rename", "bar", "baz")
assert "Cannot rename active environment" in capfd.readouterr()[0]
env("create", "qux")
# Cannot rename to an active environment (even with force flag)
with pytest.raises(spack.main.SpackCommandError):
env("rename", "-f", "qux", "bar")
assert "bar is an active environment" in capfd.readouterr()[0]
# Can rename inactive environment when another's active
out = env("rename", "qux", "quux")
assert "Successfully renamed environment qux to quux" in out
out = env("list")
assert "bar" in out
assert "baz" not in out
env("create", "baz")
# Cannot rename to existing environment without --force
with pytest.raises(spack.main.SpackCommandError):
env("rename", "bar", "baz")
errmsg = (
"The new name corresponds to an existing environment;"
" specify the --force flag to overwrite it."
)
assert errmsg in capfd.readouterr()[0]
env("rename", "-f", "bar", "baz")
out = env("list")
assert "bar" not in out
assert "baz" in out
def test_env_rename_anonymous(capfd, tmpdir):
# Need real environment
with pytest.raises(spack.main.SpackCommandError):
env("rename", "-d", "./non-existing", "./also-non-existing")
assert (
"The specified path does not correspond to a valid spack environment"
in capfd.readouterr()[0]
)
anon_foo = str(tmpdir / "foo")
env("create", "-d", anon_foo)
anon_bar = str(tmpdir / "bar")
out = env("rename", "-d", anon_foo, anon_bar)
assert f"Successfully renamed environment {anon_foo} to {anon_bar}" in out
assert not ev.is_env_dir(anon_foo)
assert ev.is_env_dir(anon_bar)
# Cannot rename active environment
anon_baz = str(tmpdir / "baz")
env("activate", "--sh", "-d", anon_bar)
with pytest.raises(spack.main.SpackCommandError):
env("rename", "-d", anon_bar, anon_baz)
assert "Cannot rename active environment" in capfd.readouterr()[0]
env("deactivate", "--sh")
assert ev.is_env_dir(anon_bar)
assert not ev.is_env_dir(anon_baz)
# Cannot rename to existing environment without --force
env("create", "-d", anon_baz)
with pytest.raises(spack.main.SpackCommandError):
env("rename", "-d", anon_bar, anon_baz)
errmsg = (
"The new path corresponds to an existing environment;"
" specify the --force flag to overwrite it."
)
assert errmsg in capfd.readouterr()[0]
assert ev.is_env_dir(anon_bar)
assert ev.is_env_dir(anon_baz)
env("rename", "-f", "-d", anon_bar, anon_baz)
assert not ev.is_env_dir(anon_bar)
assert ev.is_env_dir(anon_baz)
# Cannot rename to existing (non-environment) path without --force
qux = tmpdir / "qux"
qux.mkdir()
anon_qux = str(qux)
assert not ev.is_env_dir(anon_qux)
with pytest.raises(spack.main.SpackCommandError):
env("rename", "-d", anon_baz, anon_qux)
errmsg = "The new path already exists; specify the --force flag to overwrite it."
assert errmsg in capfd.readouterr()[0]
env("rename", "-f", "-d", anon_baz, anon_qux)
assert not ev.is_env_dir(anon_baz)
assert ev.is_env_dir(anon_qux)
def test_concretize(): def test_concretize():
e = ev.create("test") e = ev.create("test")
e.add("mpileaks") e.add("mpileaks")
@ -3133,7 +3254,7 @@ def test_create_and_activate_managed(tmp_path):
env("deactivate") env("deactivate")
def test_create_and_activate_unmanaged(tmp_path): def test_create_and_activate_anonymous(tmp_path):
with fs.working_dir(str(tmp_path)): with fs.working_dir(str(tmp_path)):
env_dir = os.path.join(str(tmp_path), "foo") env_dir = os.path.join(str(tmp_path), "foo")
shell = env("activate", "--without-view", "--create", "--sh", "-d", env_dir) shell = env("activate", "--without-view", "--create", "--sh", "-d", env_dir)

View file

@ -1032,7 +1032,7 @@ _spack_env() {
then then
SPACK_COMPREPLY="-h --help" SPACK_COMPREPLY="-h --help"
else else
SPACK_COMPREPLY="activate deactivate create remove rm list ls status st loads view update revert depfile" SPACK_COMPREPLY="activate deactivate create remove rm rename mv list ls status st loads view update revert depfile"
fi fi
} }
@ -1076,6 +1076,24 @@ _spack_env_rm() {
fi fi
} }
_spack_env_rename() {
if $list_options
then
SPACK_COMPREPLY="-h --help -d --dir -f --force"
else
SPACK_COMPREPLY=""
fi
}
_spack_env_mv() {
if $list_options
then
SPACK_COMPREPLY="-h --help -d --dir -f --force"
else
SPACK_COMPREPLY=""
fi
}
_spack_env_list() { _spack_env_list() {
SPACK_COMPREPLY="-h --help" SPACK_COMPREPLY="-h --help"
} }

View file

@ -1472,8 +1472,10 @@ complete -c spack -n '__fish_spack_using_command_pos 0 env' -f -a deactivate -d
complete -c spack -n '__fish_spack_using_command_pos 0 env' -f -a create -d 'create a new environment' complete -c spack -n '__fish_spack_using_command_pos 0 env' -f -a create -d 'create a new environment'
complete -c spack -n '__fish_spack_using_command_pos 0 env' -f -a remove -d 'remove an existing environment' complete -c spack -n '__fish_spack_using_command_pos 0 env' -f -a remove -d 'remove an existing environment'
complete -c spack -n '__fish_spack_using_command_pos 0 env' -f -a rm -d 'remove an existing environment' complete -c spack -n '__fish_spack_using_command_pos 0 env' -f -a rm -d 'remove an existing environment'
complete -c spack -n '__fish_spack_using_command_pos 0 env' -f -a list -d 'list available environments' complete -c spack -n '__fish_spack_using_command_pos 0 env' -f -a rename -d 'rename an existing environment'
complete -c spack -n '__fish_spack_using_command_pos 0 env' -f -a ls -d 'list available environments' complete -c spack -n '__fish_spack_using_command_pos 0 env' -f -a mv -d 'rename an existing environment'
complete -c spack -n '__fish_spack_using_command_pos 0 env' -f -a list -d 'list managed environments'
complete -c spack -n '__fish_spack_using_command_pos 0 env' -f -a ls -d 'list managed environments'
complete -c spack -n '__fish_spack_using_command_pos 0 env' -f -a status -d 'print whether there is an active environment' complete -c spack -n '__fish_spack_using_command_pos 0 env' -f -a status -d 'print whether there is an active environment'
complete -c spack -n '__fish_spack_using_command_pos 0 env' -f -a st -d 'print whether there is an active environment' complete -c spack -n '__fish_spack_using_command_pos 0 env' -f -a st -d 'print whether there is an active environment'
complete -c spack -n '__fish_spack_using_command_pos 0 env' -f -a loads -d 'list modules for an installed environment \'(see spack module loads)\'' complete -c spack -n '__fish_spack_using_command_pos 0 env' -f -a loads -d 'list modules for an installed environment \'(see spack module loads)\''
@ -1561,6 +1563,26 @@ complete -c spack -n '__fish_spack_using_command env rm' -s h -l help -d 'show t
complete -c spack -n '__fish_spack_using_command env rm' -s y -l yes-to-all -f -a yes_to_all complete -c spack -n '__fish_spack_using_command env rm' -s y -l yes-to-all -f -a yes_to_all
complete -c spack -n '__fish_spack_using_command env rm' -s y -l yes-to-all -d 'assume "yes" is the answer to every confirmation request' complete -c spack -n '__fish_spack_using_command env rm' -s y -l yes-to-all -d 'assume "yes" is the answer to every confirmation request'
# spack env rename
set -g __fish_spack_optspecs_spack_env_rename h/help d/dir f/force
complete -c spack -n '__fish_spack_using_command env rename' -s h -l help -f -a help
complete -c spack -n '__fish_spack_using_command env rename' -s h -l help -d 'show this help message and exit'
complete -c spack -n '__fish_spack_using_command env rename' -s d -l dir -f -a dir
complete -c spack -n '__fish_spack_using_command env rename' -s d -l dir -d 'the specified arguments correspond to directory paths'
complete -c spack -n '__fish_spack_using_command env rename' -s f -l force -f -a force
complete -c spack -n '__fish_spack_using_command env rename' -s f -l force -d 'allow overwriting of an existing environment'
# spack env mv
set -g __fish_spack_optspecs_spack_env_mv h/help d/dir f/force
complete -c spack -n '__fish_spack_using_command env mv' -s h -l help -f -a help
complete -c spack -n '__fish_spack_using_command env mv' -s h -l help -d 'show this help message and exit'
complete -c spack -n '__fish_spack_using_command env mv' -s d -l dir -f -a dir
complete -c spack -n '__fish_spack_using_command env mv' -s d -l dir -d 'the specified arguments correspond to directory paths'
complete -c spack -n '__fish_spack_using_command env mv' -s f -l force -f -a force
complete -c spack -n '__fish_spack_using_command env mv' -s f -l force -d 'allow overwriting of an existing environment'
# spack env list # spack env list
set -g __fish_spack_optspecs_spack_env_list h/help set -g __fish_spack_optspecs_spack_env_list h/help
complete -c spack -n '__fish_spack_using_command env list' -s h -l help -f -a help complete -c spack -n '__fish_spack_using_command env list' -s h -l help -f -a help