diff --git a/CHANGES.md b/CHANGES.md
index d7dbebd..f549d9a 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,5 +1,11 @@
# Changelog
+## 1.0.0a7 (unreleased)
+
+- Generic setup migration: Migrate any GenericSetup xml files in any location.
+ This fixes a problem, where upgrade steps were not migrated.
+ [thet]
+
## 1.0.0a6 (2026-03-08)
- Replace the plone.app.z3cform relateditems widget with the contentbrowser
diff --git a/src/plone_codemod/zcml_migrator.py b/src/plone_codemod/zcml_migrator.py
index 626ce53..7c0da3b 100644
--- a/src/plone_codemod/zcml_migrator.py
+++ b/src/plone_codemod/zcml_migrator.py
@@ -139,7 +139,7 @@ def migrate_genericsetup_files(
config_path: Path = CONFIG_PATH,
dry_run: bool = False,
) -> list[Path]:
- """Walk directory and migrate GenericSetup XML files (profiles/**/*.xml)."""
+ """Walk directory and migrate all GenericSetup XML files."""
config = load_config(config_path)
# Derive dotted-name replacements from the imports section
@@ -149,15 +149,11 @@ def migrate_genericsetup_files(
view_replacements = config.get("genericsetup", {}).get("view_replacements")
modified = []
- # Look for XML files in profiles/ directories and also top-level XML
+ # Look for all XML files (GenericSetup can be in various locations)
for xml_file in sorted(root.rglob("*.xml")):
- # Skip non-GenericSetup files
+ # Skip ZCML files (they are handled separately)
if ".zcml" in xml_file.suffixes:
continue
- # Focus on profiles directories and registry files
- parts_str = str(xml_file)
- if "profiles" not in parts_str and "registry.xml" not in xml_file.name:
- continue
if dry_run:
try:
diff --git a/tests/test_zcml_migrator.py b/tests/test_zcml_migrator.py
index 4877cee..127d62e 100644
--- a/tests/test_zcml_migrator.py
+++ b/tests/test_zcml_migrator.py
@@ -178,14 +178,17 @@ def test_migrate_genericsetup_files_in_profiles(self, config):
content = xml_file.read_text()
assert "plone.base.interfaces.controlpanel.IEditingSchema" in content
- def test_skips_non_profile_xml(self, config):
- """XML files outside profiles/ directories should be skipped."""
+ def test_migrate_genericsetup_files_in_other_locations(self, config):
with tempfile.TemporaryDirectory() as tmpdir:
root = Path(tmpdir)
- xml_file = root / "random.xml"
+ profiles = root / "other"
+ profiles.mkdir(parents=True)
+ xml_file = profiles / "resources.xml"
xml_file.write_text(
- ''
+ ''
)
modified = migrate_genericsetup_files(root)
- assert len(modified) == 0
+ assert len(modified) == 1
+ content = xml_file.read_text()
+ assert "plone.base.interfaces.resources.IBundleRegistry" in content