Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Addon.py
Original file line number Diff line number Diff line change
Expand Up @@ -688,7 +688,7 @@ def get_zip_url(self) -> str:
else:
# The ZIP url is based on the location of the main cache file:
if self.relative_cache_path:
cache_file_url = fci.Preferences().get("addon_catalog_cache_url")
cache_file_url = fci.Preferences().get("addon_index_cache_url")
parsed_url = urlparse(cache_file_url)
path_parts = parsed_url.path.rpartition("/")
new_path = path_parts[0] + "/" + self.relative_cache_path
Expand Down
53 changes: 47 additions & 6 deletions AddonManagerTest/app/test_workers_startup.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,17 @@ def test_no_new_catalog_available(self, mock_network_manager, mock_preferences_c
)

def get_side_effect(key):
if key == "last_fetched_addon_catalog_cache_hash":
if key == "last_fetched_addon_index_cache_hash":
return "1234567890abcdef"
elif key == "addon_catalog_cache_url":
elif key == "addon_index_cache_url":
return "https://some.url"
return None

mock_preferences_instance.get = MagicMock(side_effect=get_side_effect)

# Act
result = addonmanager_workers_startup.CreateAddonListWorker.new_cache_available(
"addon_catalog"
"addon_index"
)

# Assert
Expand All @@ -71,17 +71,17 @@ def test_new_catalog_is_available(self, mock_network_manager, mock_preferences_c
)

def get_side_effect(key):
if key == "last_fetched_addon_catalog_cache_hash":
if key == "last_fetched_addon_index_cache_hash":
return "fedcba0987654321" # NOT the same hash
elif key == "addon_catalog_cache_url":
elif key == "addon_index_cache_url":
return "https://some.url"
return None

mock_preferences_instance.get = MagicMock(side_effect=get_side_effect)

# Act
result = addonmanager_workers_startup.CreateAddonListWorker.new_cache_available(
"addon_catalog"
"addon_index"
)

# Assert
Expand Down Expand Up @@ -155,3 +155,44 @@ def test_process_addon_catalog_with_user_override(

# Assert
self.assertEqual(8, mock_addon_repo_signal.emit.call_count)

@patch("addonmanager_workers_startup.fci.Preferences")
@patch("addonmanager_workers_startup.fci.Console")
def test_migrate_catalog_to_index_no_custom_data(self, mock_console, mock_preferences_class):
# Arrange
def return_no_custom_data(key):
return None if key != "addon_catalog_cache_url" else "obsolete"

mock_preferences_instance = MagicMock()
mock_preferences_instance.get = return_no_custom_data
mock_preferences_class.return_value = mock_preferences_instance
worker = addonmanager_workers_startup.CreateAddonListWorker()

# Act
worker.migrate_catalog_to_index()

# Assert
mock_preferences_instance.set.assert_not_called()
mock_console.PrintWarning.assert_not_called()

@patch("addonmanager_workers_startup.fci.Preferences")
@patch("addonmanager_workers_startup.fci.Console")
def test_migrate_catalog_to_index_custom_data(self, mock_console, mock_preferences_class):

# Arrange
def return_custom_data(key):
return None if key != "addon_catalog_cache_url" else "some custom url"

mock_preferences_instance = MagicMock()
mock_preferences_instance.get = return_custom_data
mock_preferences_class.return_value = mock_preferences_instance
worker = addonmanager_workers_startup.CreateAddonListWorker()

# Act
worker.migrate_catalog_to_index()

# Assert
mock_preferences_instance.set.assert_called_once_with(
"addon_index_cache_url", "some custom url"
)
mock_console.PrintWarning.assert_called()
5 changes: 3 additions & 2 deletions addonmanager_preferences_defaults.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,13 @@
"ViewStyle": 1,
"WindowHeight": 600,
"WindowWidth": 800,
"addon_catalog_cache_url": "https://addons.freecad.org/addon_catalog_cache.zip",
"addon_catalog_cache_url": "obsolete, replaced by addon_index_cache_url",
"addon_index_cache_url": "https://addons.freecad.org/addon_index_cache.zip",
"alwaysAskForToolbar": true,
"dontShowAddMacroButtonDialog": false,
"force_git_in_repos": "parts_library",
"ignored_missing_deps": "",
"last_fetched_addon_catalog_cache_hash": "Cache never fetched, no hash available",
"last_fetched_addon_index_cache_hash": "Cache never fetched, no hash available",
"last_fetched_macro_cache_hash": "Cache never fetched, no hash available",
"macro_cache_url": "https://addons.freecad.org/macro_cache.zip",
"old_backup_handling": "ask",
Expand Down
15 changes: 13 additions & 2 deletions addonmanager_workers_startup.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,12 @@ def run(self):
self._get_custom_addons()
self.progress_made.emit("Custom addons loaded", 5, 100)

addon_cache = self.get_cache("addon_catalog")
CreateAddonListWorker.migrate_catalog_to_index()

addon_cache = self.get_cache("addon_index")
if addon_cache:
self.process_addon_cache(addon_cache)
self.progress_made.emit("Addon catalog loaded", 20, 100)
self.progress_made.emit("Addon index loaded", 20, 100)

macro_cache = self.get_cache("macro")
if macro_cache:
Expand All @@ -90,6 +92,15 @@ def run(self):
fci.Console.PrintError(str(e) + "\n")
return

@staticmethod
def migrate_catalog_to_index():
old_catalog_url = fci.Preferences().get("addon_catalog_cache_url")
if old_catalog_url.startswith("obsolete"):
return # Nothing to migrate, it was never set to anything else
fci.Console.PrintWarning("Custom catalog URL was detected: using as the index URL now\n")
fci.Console.PrintWarning(f"URL: {old_catalog_url}\n")
fci.Preferences().set("addon_index_cache_url", old_catalog_url)

Comment on lines +97 to +103
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

migrate_catalog_to_index() will keep running on every startup for users who previously set a custom addon_catalog_cache_url (since the old value stays non-"obsolete"). That means it will repeatedly overwrite any user-chosen addon_index_cache_url and re-print warnings each run. Make this migration one-time (e.g., only migrate when addon_index_cache_url is still the default, and/or mark the old key as obsolete after copying, or add an explicit migration flag) so it doesn’t clobber later user configuration.

Suggested change
old_catalog_url = fci.Preferences().get("addon_catalog_cache_url")
if old_catalog_url.startswith("obsolete"):
return # Nothing to migrate, it was never set to anything else
fci.Console.PrintWarning("Custom catalog URL was detected: using as the index URL now\n")
fci.Console.PrintWarning(f"URL: {old_catalog_url}\n")
fci.Preferences().set("addon_index_cache_url", old_catalog_url)
prefs = fci.Preferences()
old_catalog_url = prefs.get("addon_catalog_cache_url")
if not old_catalog_url or old_catalog_url.startswith("obsolete"):
return # Nothing to migrate, it was never set to anything else or was already migrated
fci.Console.PrintWarning("Custom catalog URL was detected: using as the index URL now\n")
fci.Console.PrintWarning(f"URL: {old_catalog_url}\n")
prefs.set("addon_index_cache_url", old_catalog_url)
# Mark the old key as obsolete so this migration only runs once and does not
# overwrite any future user configuration of addon_index_cache_url.
prefs.set("addon_catalog_cache_url", f"obsolete:{old_catalog_url}")

Copilot uses AI. Check for mistakes.
def _get_custom_addons(self):

# querying custom addons first
Expand Down
Loading