Skip to content

refactor: migration.#9191

Open
camilasan wants to merge 8 commits intomasterfrom
enh/migration
Open

refactor: migration.#9191
camilasan wants to merge 8 commits intomasterfrom
enh/migration

Conversation

@camilasan
Copy link
Member

@camilasan camilasan commented Dec 4, 2025

Replaces work in #8154.

It moves code around and add some very basics tests. More work is needed to be able to fully cover the logic with tests.

/**
* => Application::configVersionMigration: check existing config
* => no upgrade/downgrade/migration: Migration::Phase::Done
* => need to set config file: Migration::Phase::SetupConfigFile
* => Application::setupAccountsAndFolders: Migration::Phase::SetupUsers
* => Application::restoreLegacyAccount
* => AccountManager::restore
* => AccountManager::restoreFromLegacySettings:
* => Migration::legacyData: find legacy config and read its settings using
* => FolderMan::setupFolders: Migration::Phase::SetupFolders
* => AccountState::slotCredentialsFetched: Migration::Phase::Done
*/

@camilasan camilasan marked this pull request as draft December 4, 2025 21:19
@sonarqubecloud
Copy link

sonarqubecloud bot commented Dec 4, 2025

Quality Gate Failed Quality Gate failed

Failed conditions
28.4% Coverage on New Code (required ≥ 80%)
127 New Code Smells (required ≤ 0)
D Security Rating on New Code (required ≥ A)
C Maintainability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

@camilasan camilasan changed the title WIP: refactor: migration refactor: migration. Jan 9, 2026
@camilasan camilasan added this to the 33.0.0 milestone Jan 21, 2026
@camilasan camilasan removed this from the 33.0.0 milestone Feb 17, 2026
It should cover upgrade, downgrade as well as upgrade from unbranded to branded,
from legacy to branded and from legacy to unbranded.

Signed-off-by: Camila Ayres <hello@camilasan.com>
Signed-off-by: Camila Ayres <hello@camilasan.com>
If no legacy file is found, it means it might be upgrade only.

Signed-off-by: Camila Ayres <hello@camilasan.com>
…to ConfigFile.

Signed-off-by: Camila Ayres <hello@camilasan.com>
…n class.

- Make all Migration class members static.
- Rename Migration class members.

Signed-off-by: Camila Ayres <hello@camilasan.com>
Signed-off-by: Camila Ayres <hello@camilasan.com>
@camilasan camilasan requested a review from Copilot March 3, 2026 21:11
@camilasan camilasan marked this pull request as ready for review March 3, 2026 21:11
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR refactors client migration logic by moving migration state/decision-making out of ConfigFile into a new OCC::Migration helper, and wires it through GUI/libsync code paths. It also adds a new unit test target for migration behavior.

Changes:

  • Introduces src/libsync/settings/migration.{h,cpp} and updates build/test wiring.
  • Replaces ConfigFile migration helpers with Migration usage across Application/AccountManager/AccountState/FolderMan.
  • Adds ConfigFile::backupConfigFiles() and a new Migration test.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 13 comments.

Show a summary per file
File Description
test/testmigration.cpp Adds a new migration-focused test (currently with correctness issues).
test/CMakeLists.txt Registers the new Migration test target.
src/libsync/settings/migration.h Declares new Migration API and static migration state.
src/libsync/settings/migration.cpp Implements version checks and legacy config discovery.
src/libsync/configfile.h Removes old migration API surface; adds backupConfigFiles().
src/libsync/configfile.cpp Switches to Migration for branded/unbranded checks; adds backupConfigFiles().
src/libsync/CMakeLists.txt Adds new migration sources to libsync build.
src/gui/folderman.h Adds TestMigration friend declaration.
src/gui/folderman.cpp Uses Migration::discoveredLegacyConfigPath() for folder migration path selection.
src/gui/application.cpp Uses Migration phases/flags during config migration and setup flow.
src/gui/accountstate.cpp Uses Migration for branded/unbranded migration keychain handling and phase completion.
src/gui/accountmanager.cpp Refactors legacy-settings restore flow to use Migration::legacyData().

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 524 to 525
&& !AccountManager::instance()->forceLegacyImport()
&& accountsListSize > 0) {
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

The condition redundantly checks accountsListSize > 0 twice. Drop the duplicate to keep the logic minimal and avoid mistakes when editing this condition later.

Suggested change
&& !AccountManager::instance()->forceLegacyImport()
&& accountsListSize > 0) {
&& !AccountManager::instance()->forceLegacyImport()) {

Copilot uses AI. Check for mistakes.
Comment on lines +141 to +142
folderDefinition.localPath = "/standardAppName";
folderDefinition.targetPath = "/";
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

folderDefinition.localPath is set to the literal "/standardAppName", which won’t exist on most systems. Since Folder::checkLocalPath() requires the directory to exist and be readable, this will make the test fail in CI. Use a path under _temporaryDir.path() (and mkpath it) for the local sync folder.

Suggested change
folderDefinition.localPath = "/standardAppName";
folderDefinition.targetPath = "/";
const auto localPath = _temporaryDir.path() + QLatin1Char('/') + QLatin1String(standardAppName);
QVERIFY(QDir().mkpath(localPath));
folderDefinition.localPath = localPath;
folderDefinition.targetPath = QLatin1String("/");

Copilot uses AI. Check for mistakes.
Comment on lines +47 to +50
"0\\Folders\2ba4b09a-1223-aaaa-abcd-c2df238816d8\\ignoreHiddenFiles=true\n"
"0\\Folders\2ba4b09a-1223-aaaa-abcd-c2df238816d8\\journalPath=.sync_journal.db\n"
"0\\Folders\2ba4b09a-1223-aaaa-abcd-c2df238816d8\\localPath=/ownCloud/\n"
"0\\Folders\2ba4b09a-1223-aaaa-abcd-c2df238816d8\\paused=false\n"
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

Several lines in legacyAppConfigContent use a single backslash before 2ba... (e.g. "0\\Folders\2ba..."). In C++ string literals \2 is interpreted as an octal escape, so the resulting INI content is corrupted. Escape the backslashes consistently (use \\ where you want a literal \ in the INI).

Suggested change
"0\\Folders\2ba4b09a-1223-aaaa-abcd-c2df238816d8\\ignoreHiddenFiles=true\n"
"0\\Folders\2ba4b09a-1223-aaaa-abcd-c2df238816d8\\journalPath=.sync_journal.db\n"
"0\\Folders\2ba4b09a-1223-aaaa-abcd-c2df238816d8\\localPath=/ownCloud/\n"
"0\\Folders\2ba4b09a-1223-aaaa-abcd-c2df238816d8\\paused=false\n"
"0\\Folders\\2ba4b09a-1223-aaaa-abcd-c2df238816d8\\ignoreHiddenFiles=true\n"
"0\\Folders\\2ba4b09a-1223-aaaa-abcd-c2df238816d8\\journalPath=.sync_journal.db\n"
"0\\Folders\\2ba4b09a-1223-aaaa-abcd-c2df238816d8\\localPath=/ownCloud/\n"
"0\\Folders\\2ba4b09a-1223-aaaa-abcd-c2df238816d8\\paused=false\n"

Copilot uses AI. Check for mistakes.
qCDebug(lcMigration) << "Copy settings" << oCSettings->allKeys().join(", ");
Migration migration;
migration.setDiscoveredLegacyConfigPath(configFileInfo.canonicalPath());
legacyData.reset(oCSettings.get());
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

legacyData.reset(oCSettings.get()) assigns a raw pointer still owned by std::unique_ptr<QSettings> oCSettings, which will be deleted at scope exit. This results in double-delete / use-after-free. Transfer ownership explicitly (e.g., construct the shared pointer from oCSettings.release()), or avoid mixing unique_ptr and QSharedPointer here.

Suggested change
legacyData.reset(oCSettings.get());
legacyData.reset(oCSettings.release());

Copilot uses AI. Check for mistakes.
_configFile.setConfDir(standardConfigFolder);
}

void setupStandarConfig(const QString &version)
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

Function name typo: setupStandarConfig (missing ‘d’) makes the test harder to read/search and looks unintentional. Rename to setupStandardConfig (and update call sites).

Suggested change
void setupStandarConfig(const QString &version)
void setupStandardConfig(const QString &version)

Copilot uses AI. Check for mistakes.
_configFile.setOverrideLocalDir("A");
_configFile.setVfsEnabled(true);
_configFile.setProxyType(0);
_configFile.setVfsEnabled(true);
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

Duplicate call: setVfsEnabled(true) is invoked twice (lines 96 and 98). Remove the redundant call to keep the test setup clear.

Suggested change
_configFile.setVfsEnabled(true);

Copilot uses AI. Check for mistakes.
_configFile.setConfDir(standardConfigFolder);
}

void setupStandarConfig(const QString &version)
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

Function name typo: setupStandarConfig (missing ‘d’) makes the test harder to read/search and looks unintentional. Rename to setupStandardConfig (and update call sites).

Suggested change
void setupStandarConfig(const QString &version)
void setupStandardConfig(const QString &version)

Copilot uses AI. Check for mistakes.
"logHttp=false\n"
"optionalDesktopNotifications=true\n"
"\n"
"[Accounts]e\n"
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

legacyAppConfigContent has a typo in the INI section header: "[Accounts]e" includes an extra e, which will break parsing if this test data is used. Remove the stray character.

Suggested change
"[Accounts]e\n"
"[Accounts]\n"

Copilot uses AI. Check for mistakes.
Comment on lines +40 to +43
};

using LegacyData = QSharedPointer<QSettings>;

Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

Migration::LegacyData is QSharedPointer<QSettings>, but this header doesn’t include <QSharedPointer>. Depending on include order this can fail to compile. Add the missing include (and consider dropping unused includes like <QMap> if not needed).

Copilot uses AI. Check for mistakes.
Comment on lines 254 to 257
}

settings.reset(oCSettings.get());
}
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

settings.reset(oCSettings.get()) makes the std::unique_ptr take ownership of a pointer that is still owned by oCSettings (QSharedPointer). This will lead to double deletion. Use a single ownership model (e.g., keep settings as a shared pointer, or have Migration::legacyData() return a std::unique_ptr<QSettings> and move it into settings) instead of resetting from get().

Copilot uses AI. Check for mistakes.
Signed-off-by: Camila Ayres <hello@camilasan.com>
Signed-off-by: Camila Ayres <hello@camilasan.com>
@github-actions
Copy link

github-actions bot commented Mar 3, 2026

Artifact containing the AppImage: nextcloud-appimage-pr-9191.zip

Digest: sha256:7e5932761596faa513e52c1b5b515feda9da327a01c23c3505802d2c77d7ae6e

To test this change/fix you can download the above artifact file, unzip it, and run it.

Please make sure to quit your existing Nextcloud app and backup your data.

@sonarqubecloud
Copy link

sonarqubecloud bot commented Mar 3, 2026

Quality Gate Failed Quality Gate failed

Failed conditions
38.1% Coverage on New Code (required ≥ 80%)
C Maintainability Rating on New Code (required ≥ A)
1 New Bugs (required ≤ 0)
C Reliability Rating on New Code (required ≥ A)
D Security Rating on New Code (required ≥ A)
186 New Code Smells (required ≤ 0)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants