spack -C <env>: use env config w/o activation (#45046)
Precedence: 1. Named environment 2. Anonymous environment 3. Generic directory
This commit is contained in:
parent
aeaa922eef
commit
8a430f89b3
@ -796,22 +796,27 @@ def config_paths_from_entry_points() -> List[Tuple[str, str]]:
|
||||
def _add_command_line_scopes(
|
||||
cfg: Union[Configuration, lang.Singleton], command_line_scopes: List[str]
|
||||
) -> None:
|
||||
"""Add additional scopes from the --config-scope argument.
|
||||
"""Add additional scopes from the --config-scope argument, either envs or dirs."""
|
||||
import spack.environment.environment as env # circular import
|
||||
|
||||
Command line scopes are named after their position in the arg list.
|
||||
"""
|
||||
for i, path in enumerate(command_line_scopes):
|
||||
# We ensure that these scopes exist and are readable, as they are
|
||||
# provided on the command line by the user.
|
||||
if not os.path.isdir(path):
|
||||
raise ConfigError(f"config scope is not a directory: '{path}'")
|
||||
elif not os.access(path, os.R_OK):
|
||||
raise ConfigError(f"config scope is not readable: '{path}'")
|
||||
name = f"cmd_scope_{i}"
|
||||
|
||||
# name based on order on the command line
|
||||
name = f"cmd_scope_{i:d}"
|
||||
cfg.push_scope(DirectoryConfigScope(name, path, writable=False))
|
||||
_add_platform_scope(cfg, name, path, writable=False)
|
||||
if env.exists(path): # managed environment
|
||||
manifest = env.EnvironmentManifestFile(env.root(path))
|
||||
elif env.is_env_dir(path): # anonymous environment
|
||||
manifest = env.EnvironmentManifestFile(path)
|
||||
elif os.path.isdir(path): # directory with config files
|
||||
cfg.push_scope(DirectoryConfigScope(name, path, writable=False))
|
||||
_add_platform_scope(cfg, name, path, writable=False)
|
||||
continue
|
||||
else:
|
||||
raise ConfigError(f"Invalid configuration scope: {path}")
|
||||
|
||||
for scope in manifest.env_config_scopes:
|
||||
scope.name = f"{name}:{scope.name}"
|
||||
scope.writable = False
|
||||
cfg.push_scope(scope)
|
||||
|
||||
|
||||
def create() -> Configuration:
|
||||
|
@ -269,9 +269,7 @@ def root(name):
|
||||
|
||||
def exists(name):
|
||||
"""Whether an environment with this name exists or not."""
|
||||
if not valid_env_name(name):
|
||||
return False
|
||||
return os.path.isdir(root(name))
|
||||
return valid_env_name(name) and os.path.isdir(_root(name))
|
||||
|
||||
|
||||
def active(name):
|
||||
@ -922,7 +920,7 @@ def __init__(self, manifest_dir: Union[str, pathlib.Path]) -> None:
|
||||
def _load_manifest_file(self):
|
||||
"""Instantiate and load the manifest file contents into memory."""
|
||||
with lk.ReadTransaction(self.txlock):
|
||||
self.manifest = EnvironmentManifestFile(self.path)
|
||||
self.manifest = EnvironmentManifestFile(self.path, self.name)
|
||||
with self.manifest.use_config():
|
||||
self._read()
|
||||
|
||||
@ -2753,10 +2751,11 @@ def from_lockfile(manifest_dir: Union[pathlib.Path, str]) -> "EnvironmentManifes
|
||||
manifest.flush()
|
||||
return manifest
|
||||
|
||||
def __init__(self, manifest_dir: Union[pathlib.Path, str]) -> None:
|
||||
def __init__(self, manifest_dir: Union[pathlib.Path, str], name: Optional[str] = None) -> None:
|
||||
self.manifest_dir = pathlib.Path(manifest_dir)
|
||||
self.name = name or str(manifest_dir)
|
||||
self.manifest_file = self.manifest_dir / manifest_name
|
||||
self.scope_name = f"env:{environment_name(self.manifest_dir)}"
|
||||
self.scope_name = f"env:{self.name}"
|
||||
self.config_stage_dir = os.path.join(env_subdir_path(manifest_dir), "config")
|
||||
|
||||
#: Configuration scopes associated with this environment. Note that these are not
|
||||
@ -3033,7 +3032,6 @@ def included_config_scopes(self) -> List[spack.config.ConfigScope]:
|
||||
# load config scopes added via 'include:', in reverse so that
|
||||
# highest-precedence scopes are last.
|
||||
includes = self[TOP_LEVEL_KEY].get("include", [])
|
||||
env_name = environment_name(self.manifest_dir)
|
||||
missing = []
|
||||
for i, config_path in enumerate(reversed(includes)):
|
||||
# allow paths to contain spack config/environment variables, etc.
|
||||
@ -3096,12 +3094,12 @@ def included_config_scopes(self) -> List[spack.config.ConfigScope]:
|
||||
|
||||
if os.path.isdir(config_path):
|
||||
# directories are treated as regular ConfigScopes
|
||||
config_name = "env:%s:%s" % (env_name, os.path.basename(config_path))
|
||||
config_name = f"env:{self.name}:{os.path.basename(config_path)}"
|
||||
tty.debug(f"Creating DirectoryConfigScope {config_name} for '{config_path}'")
|
||||
scopes.append(spack.config.DirectoryConfigScope(config_name, config_path))
|
||||
elif os.path.exists(config_path):
|
||||
# files are assumed to be SingleFileScopes
|
||||
config_name = "env:%s:%s" % (env_name, config_path)
|
||||
config_name = f"env:{self.name}:{config_path}"
|
||||
tty.debug(f"Creating SingleFileScope {config_name} for '{config_path}'")
|
||||
scopes.append(
|
||||
spack.config.SingleFileScope(
|
||||
|
@ -444,8 +444,9 @@ def make_argument_parser(**kwargs):
|
||||
"--config-scope",
|
||||
dest="config_scopes",
|
||||
action="append",
|
||||
metavar="DIR",
|
||||
help="add a custom configuration scope",
|
||||
metavar="DIR|ENV",
|
||||
help="add directory or environment as read-only configuration scope, without activating "
|
||||
"the environment.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-d",
|
||||
|
@ -13,7 +13,7 @@
|
||||
import pytest
|
||||
|
||||
import llnl.util.tty as tty
|
||||
from llnl.util.filesystem import getuid, join_path, mkdirp, touch, touchp
|
||||
from llnl.util.filesystem import join_path, touch, touchp
|
||||
|
||||
import spack.config
|
||||
import spack.directory_layout
|
||||
@ -864,26 +864,18 @@ def test_bad_config_section(mock_low_high_config):
|
||||
spack.config.get("foobar")
|
||||
|
||||
|
||||
@pytest.mark.not_on_windows("chmod not supported on Windows")
|
||||
@pytest.mark.skipif(getuid() == 0, reason="user is root")
|
||||
def test_bad_command_line_scopes(tmpdir, config):
|
||||
def test_bad_command_line_scopes(tmp_path, config):
|
||||
cfg = spack.config.Configuration()
|
||||
file_path = tmp_path / "file_instead_of_dir"
|
||||
non_existing_path = tmp_path / "non_existing_dir"
|
||||
|
||||
with tmpdir.as_cwd():
|
||||
with pytest.raises(spack.config.ConfigError):
|
||||
spack.config._add_command_line_scopes(cfg, ["bad_path"])
|
||||
file_path.write_text("")
|
||||
|
||||
touch("unreadable_file")
|
||||
with pytest.raises(spack.config.ConfigError):
|
||||
spack.config._add_command_line_scopes(cfg, ["unreadable_file"])
|
||||
with pytest.raises(spack.config.ConfigError):
|
||||
spack.config._add_command_line_scopes(cfg, [str(file_path)])
|
||||
|
||||
mkdirp("unreadable_dir")
|
||||
with pytest.raises(spack.config.ConfigError):
|
||||
try:
|
||||
os.chmod("unreadable_dir", 0)
|
||||
spack.config._add_command_line_scopes(cfg, ["unreadable_dir"])
|
||||
finally:
|
||||
os.chmod("unreadable_dir", 0o700) # so tmpdir can be removed
|
||||
with pytest.raises(spack.config.ConfigError):
|
||||
spack.config._add_command_line_scopes(cfg, [str(non_existing_path)])
|
||||
|
||||
|
||||
def test_add_command_line_scopes(tmpdir, mutable_config):
|
||||
@ -898,6 +890,45 @@ def test_add_command_line_scopes(tmpdir, mutable_config):
|
||||
)
|
||||
|
||||
spack.config._add_command_line_scopes(mutable_config, [str(tmpdir)])
|
||||
assert mutable_config.get("config:verify_ssl") is False
|
||||
assert mutable_config.get("config:dirty") is False
|
||||
|
||||
|
||||
def test_add_command_line_scope_env(tmp_path, mutable_mock_env_path):
|
||||
"""Test whether --config-scope <env> works, either by name or path."""
|
||||
managed_env = ev.create("example").manifest_path
|
||||
|
||||
with open(managed_env, "w") as f:
|
||||
f.write(
|
||||
"""\
|
||||
spack:
|
||||
config:
|
||||
install_tree:
|
||||
root: /tmp/first
|
||||
"""
|
||||
)
|
||||
|
||||
with open(tmp_path / "spack.yaml", "w") as f:
|
||||
f.write(
|
||||
"""\
|
||||
spack:
|
||||
config:
|
||||
install_tree:
|
||||
root: /tmp/second
|
||||
"""
|
||||
)
|
||||
|
||||
config = spack.config.Configuration()
|
||||
spack.config._add_command_line_scopes(config, ["example", str(tmp_path)])
|
||||
assert len(config.scopes) == 2
|
||||
assert config.get("config:install_tree:root") == "/tmp/second"
|
||||
|
||||
config = spack.config.Configuration()
|
||||
spack.config._add_command_line_scopes(config, [str(tmp_path), "example"])
|
||||
assert len(config.scopes) == 2
|
||||
assert config.get("config:install_tree:root") == "/tmp/first"
|
||||
|
||||
assert ev.active_environment() is None # shouldn't cause an environment to be activated
|
||||
|
||||
|
||||
def test_nested_override():
|
||||
|
@ -438,7 +438,7 @@ complete -c spack -n '__fish_spack_using_command ' -l color -r -d 'when to color
|
||||
complete -c spack -n '__fish_spack_using_command ' -s c -l config -r -f -a config_vars
|
||||
complete -c spack -n '__fish_spack_using_command ' -s c -l config -r -d 'add one or more custom, one off config settings'
|
||||
complete -c spack -n '__fish_spack_using_command ' -s C -l config-scope -r -f -a config_scopes
|
||||
complete -c spack -n '__fish_spack_using_command ' -s C -l config-scope -r -d 'add a custom configuration scope'
|
||||
complete -c spack -n '__fish_spack_using_command ' -s C -l config-scope -r -d 'add directory or environment as read-only configuration scope, without activating the environment.'
|
||||
complete -c spack -n '__fish_spack_using_command ' -s d -l debug -f -a debug
|
||||
complete -c spack -n '__fish_spack_using_command ' -s d -l debug -d 'write out debug messages'
|
||||
complete -c spack -n '__fish_spack_using_command ' -l timestamp -f -a timestamp
|
||||
|
Loading…
Reference in New Issue
Block a user