Make spack config update work on environments (#36542)

Previously `spack -e bla config update <section>` would treat the
environment config scope as standard config file instead of a single
file config scope. This fixes that.
This commit is contained in:
Harmen Stoppels 2023-04-04 10:19:05 +02:00
parent 030bce9978
commit 23bf0a316c
3 changed files with 46 additions and 22 deletions

View File

@ -244,30 +244,35 @@ def config_remove(args):
spack.config.set(path, existing, scope) spack.config.set(path, existing, scope)
def _can_update_config_file(scope_dir, cfg_file): def _can_update_config_file(scope, cfg_file):
dir_ok = fs.can_write_to_dir(scope_dir) if isinstance(scope, spack.config.SingleFileScope):
cfg_ok = fs.can_access(cfg_file) return fs.can_access(cfg_file)
return dir_ok and cfg_ok return fs.can_write_to_dir(scope.path) and fs.can_access(cfg_file)
def config_update(args): def config_update(args):
# Read the configuration files # Read the configuration files
spack.config.config.get_config(args.section, scope=args.scope) spack.config.config.get_config(args.section, scope=args.scope)
updates = spack.config.config.format_updates[args.section] updates = list(
filter(
lambda s: not isinstance(
s, (spack.config.InternalConfigScope, spack.config.ImmutableConfigScope)
),
spack.config.config.format_updates[args.section],
)
)
cannot_overwrite, skip_system_scope = [], False cannot_overwrite, skip_system_scope = [], False
for scope in updates: for scope in updates:
cfg_file = spack.config.config.get_config_filename(scope.name, args.section) cfg_file = spack.config.config.get_config_filename(scope.name, args.section)
scope_dir = scope.path can_be_updated = _can_update_config_file(scope, cfg_file)
can_be_updated = _can_update_config_file(scope_dir, cfg_file)
if not can_be_updated: if not can_be_updated:
if scope.name == "system": if scope.name == "system":
skip_system_scope = True skip_system_scope = True
msg = ( tty.warn(
'Not enough permissions to write to "system" scope. ' 'Not enough permissions to write to "system" scope. '
"Skipping update at that location [cfg={0}]" "Skipping update at that location [cfg={0}]".format(cfg_file)
) )
tty.warn(msg.format(cfg_file))
continue continue
cannot_overwrite.append((scope, cfg_file)) cannot_overwrite.append((scope, cfg_file))
@ -315,18 +320,14 @@ def config_update(args):
# Get a function to update the format # Get a function to update the format
update_fn = spack.config.ensure_latest_format_fn(args.section) update_fn = spack.config.ensure_latest_format_fn(args.section)
for scope in updates: for scope in updates:
cfg_file = spack.config.config.get_config_filename(scope.name, args.section) data = scope.get_section(args.section).pop(args.section)
with open(cfg_file) as f:
data = syaml.load_config(f) or {}
data = data.pop(args.section, {})
update_fn(data) update_fn(data)
# Make a backup copy and rewrite the file # Make a backup copy and rewrite the file
bkp_file = cfg_file + ".bkp" bkp_file = cfg_file + ".bkp"
shutil.copy(cfg_file, bkp_file) shutil.copy(cfg_file, bkp_file)
spack.config.config.update_config(args.section, data, scope=scope.name, force=True) spack.config.config.update_config(args.section, data, scope=scope.name, force=True)
msg = 'File "{0}" updated [backup={1}]' tty.msg('File "{}" update [backup={}]'.format(cfg_file, bkp_file))
tty.msg(msg.format(cfg_file, bkp_file))
def _can_revert_update(scope_dir, cfg_file, bkp_file): def _can_revert_update(scope_dir, cfg_file, bkp_file):

View File

@ -36,10 +36,11 @@
import re import re
import sys import sys
from contextlib import contextmanager from contextlib import contextmanager
from typing import List # novm from typing import List # novm # noqa: F401
import ruamel.yaml as yaml import ruamel.yaml as yaml
import six import six
from ruamel.yaml.comments import Comment
from ruamel.yaml.error import MarkedYAMLError from ruamel.yaml.error import MarkedYAMLError
from six import iteritems from six import iteritems
@ -532,16 +533,14 @@ def update_config(self, section, update_data, scope=None, force=False):
scope = self._validate_scope(scope) # get ConfigScope object scope = self._validate_scope(scope) # get ConfigScope object
# manually preserve comments # manually preserve comments
need_comment_copy = section in scope.sections and scope.sections[section] is not None need_comment_copy = section in scope.sections and scope.sections[section]
if need_comment_copy: if need_comment_copy:
comments = getattr( comments = getattr(scope.sections[section][section], Comment.attrib, None)
scope.sections[section][section], yaml.comments.Comment.attrib, None
)
# read only the requested section's data. # read only the requested section's data.
scope.sections[section] = syaml.syaml_dict({section: update_data}) scope.sections[section] = syaml.syaml_dict({section: update_data})
if need_comment_copy and comments: if need_comment_copy and comments:
setattr(scope.sections[section][section], yaml.comments.Comment.attrib, comments) setattr(scope.sections[section][section], Comment.attrib, comments)
scope._write_section(section) scope._write_section(section)

View File

@ -13,6 +13,7 @@
import spack.database import spack.database
import spack.environment as ev import spack.environment as ev
import spack.main import spack.main
import spack.schema.config
import spack.spec import spack.spec
import spack.store import spack.store
import spack.util.spack_yaml as syaml import spack.util.spack_yaml as syaml
@ -652,3 +653,26 @@ def test_config_prefer_upstream(
# Make sure a message about the conflicting hdf5's was given. # Make sure a message about the conflicting hdf5's was given.
assert "- hdf5" in output assert "- hdf5" in output
def test_environment_config_update(tmpdir, mutable_config, monkeypatch):
with open(str(tmpdir.join("spack.yaml")), "w") as f:
f.write(
"""\
spack:
config:
ccache: true
"""
)
def update_config(data):
data["ccache"] = False
return True
monkeypatch.setattr(spack.schema.config, "update", update_config)
with ev.Environment(str(tmpdir)):
config("update", "-y", "config")
with ev.Environment(str(tmpdir)) as e:
assert not e.raw_yaml["spack"]["config"]["ccache"]