I’m sure many of you have read the blog post I wrote about sandboxing. Now that the Sailfish OS 4.3.0 has reached the Early Access stage, you can also move to the next stage with your app: Defining the profile for your app.
Like I mentioned in the blog, we used to have application configuration files in directories named in the format .config/harbour-appname. In the future we no longer want that - the configuration files should instead be located in directories with the format .config/OrganizationName/ApplicationName. So that’s where you should write your configurations from now on. But what about the old configuration files which were already written in .config/harbour-appname? Do we lose access to them? Fear not, that is not the case!
Starting with Sailfish OS 4.3.0, if the .config/harbour-appname directory exists, it will also be visible in the sandbox. So basically, this is what you should do in order to migrate your configuration:
If your configuration files exist in .config/OrganizationName/ApplicationName, use them
If not, check if they exist in .config/harbour-appname, and if they do, read them from there
Write configuration files to .config/OrganizationName/ApplicationName
You can use something like the following code to migrate your configuration from the old location to the new:
void MySettings::migrateSettings()
{
// The new location of config file
QSettings settings(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + "/my.domain/MyApp/MyApp.conf", QSettings::NativeFormat);
if (settings.contains("migrated"))
return;
QSettings oldSettings(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + "/harbour-myapp/harbour-myapp.conf", QSettings::NativeFormat);
for (const QString& key : oldSettings.childKeys())
settings.setValue(key, oldSettings.value(key));
settings.setValue("migrated", "true");
}
Similar migration strategy could be used also for .local/share and .cache directories - you may also want to consider just copying the files over.
Dear @vige, you might clarify if a fresh installation of extant apps, which are not explicitly updated for the sandboxing yet, will still be able to create their directories ~/{.local/share,.config,.cache}/packagename and files in it on SailfishOS ≥ 4.3.0?
Currently you only address “configuration files which were already written in .config/harbour-appname”, which supposedly means “before upgrading to SailfishOS 4.3.0”.
Mind that many apps in the Jolla Store and at Openrepos are not well maintained or unmaintained, although still well working, and if they are not installable any more on recent SailfishOS releases, this will significantly reduce the already slim set of available apps for SailfishOS.
Hence backward compatibility is crucial.
My understanding, which is based on empirical testing on current code (which hopefully becomes release 4.4.0), is that the sailjail implementation creates the harbour-packagename directories for the applications which have not specified their own profiles. For the applications which have specified their profiles, the directories are not created, but are still mounted if they exist.
Thank you very much, that is exactly the clarification / unambiguous wording I wanted to see documented.
Plus WRT its content it is exactly what I hoped for (but became seriously wary).
And as you are the chief designer of SailJail AFAIU, I assume that this also is the intended behaviour, even if the implementation may deviate, right?
P.S.: I am really glad that this is fine on this abstract level, for the more specific / detailed questions and concerns, see the other, older thread.
Is there a similar “Best Practice” solution for pure QML apps (or QML+Python or other non-Qt/QSettings applications), especially those using JS LocalStorage?
So, if my app currently does something like:
invoker --type=silica-qt5 sailfish-qml my-appname
to launch, which changes are needed apart from making it
No there isn’t. For the time being, my recommendation for the old pure QML apps is to not define their own profiles, i.e. keep using the old locations. For new apps I’d recommend defining the profiles and using the new locations.
I think the answer to all of these is “no”.
As long as you are using libsailfishapp, the values are set automatically, using the values from the .desktop files.
For ApplicationName: Allowed characters are A-Z, a-z, 0-9, underscore (_) and hyphen(-). The name may not start with a number.
For OrganizationName: Same characters as ApplicationName, and a dot (.), which is used for separating components. Any component may not start with a number. There are some names which are not allowed. Currently the list of disallowed OrganizationNames contains only “com.jolla” and “org.sailfishos”.
The ApplicationName in the X-Sailjail section of the .desktop file does not have to be harbour-prefixed. This in turn means that the directory names in ~/{.local/share,.config,.cache} also no longer need to have the harbour- prefix. For the RPM package name and the binary name the old requirements still apply.
Yea, and I distinctly remember there was a similar migration a long time ago when all apps were moved out of their “orga name” subdirectories. Was that early SailfishOS? Or even Meego Harmattan times? I forget.
If you, like me, have been using QML LocalStorage instead of Settings, @vige’s nice example isn’t applicable right off the bat, so here is my poor attempt at doing the same, but for LocalStorage:
void migrateLocalStorage()
{
// The new location of the LocalStorage database
QDir newDbDir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/my.domain/MyApp/QML/OfflineStorage/Databases/");
if(newDbDir.exists())
return;
newDbDir.mkpath(newDbDir.path());
QString dbname = QString(QCryptographicHash::hash(("MyAppDB"), QCryptographicHash::Md5).toHex());
// The old LocalStorage database
QFile oldDb(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/harbour-myapp/harbour-MyApp/QML/OfflineStorage/Databases/" + dbname + ".sqlite");
QFile oldIni(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/harbour-myapp/harbour-MyApp/QML/OfflineStorage/Databases/" + dbname + ".ini");
oldDb.copy(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/my.domain/MyApp/QML/OfflineStorage/Databases/" + dbname + ".sqlite");
oldIni.copy(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/my.domain/MyApp/QML/OfflineStorage/Databases/" + dbname + ".ini");
}
I’ve managed using attah’s approach to migrate the data and it seems the naming of takes. But while debugging with the QML LiveBench I don’t appear to have access to the new data. I kept the app name for the time being, but:
.pragma library
.import QtQuick.LocalStorage 2.0 as LS
…
var db = LS.LocalStorage.openDatabaseSync(“harbour-dwd”, “1.0”, “DWD Location Cache”, 10000);
Does not work. Do I need to adapt opening the DB in js? No, this is not completely true. I do have read accesss.
EDIT: I’m wrong. All is fine. Sorry for the white noise. For a fully functional version of Attah’s suggestion:
I’ve started playing a bit with sandboxing, and ran into the following issue.
copying from the old configuration directory works fine
deleting the content inside the old configuration directory works fine
deleting the old configuration directory itself does not work if the app is sandboxed (works fine if it is not)
So, to offer a smooth migration path (that correctly cleans up any remnants of the old scheme), i must first release a version without sandboxing, that will do the migration, and then a new version that enables sandboxing. Is this correct or am i missing something ? (or is it just expected that the old directory stays there, after all it is not deleted when we uninstall apps…).
Yup. that’s my experience. I hadn’t got there, but I was thinking of trying to move rather than copy and keep a tmp directory with a backup in tld.orgname/tmp. then the worst case is old dangling directories, but no excessive data use.
EDIT: If I’m not mistaken, bool QFile::rename(const QString &newName) instead of copy should do?
EDIT 2: So, just did a test. rename is in fact mv. But I only tested it as above, doing rename instead of copy. That leaves the old folders in place.
EDIT 3: So, for really elegant migration handling @karry :
Something to be careful of is that, from my experience, the settings directory is automatically created when running through sailjail (my guess is that, to be bound to the jail container, it needs to exists). So, checking against its existence is not something to do if the app is run under sailjail, it will always exists.
On the otherhand localstorage and .local/share/appname are not automatically created. But this is also a nice approach! Thanks!
EDIT: It just occured to me, if it’s necessary to bind to the user config dirs, shouldn’t that logic apply to local storage. Sounds like a security issue. Sigh.
EDIT2: Julien’s code not from the commit but in a block:
QDir dir(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation));
if (dir.exists("harbour-kontroller"))
{
dir.mkpath("tgcm.eu/Kontroller");
if (dir.exists("harbour-kontroller/kontroller.conf"))
{
dir.rename("harbour-kontroller/kontroller.conf", "tgcm.eu/Kontroller/Kontroller.conf");
}
dir.rename("harbour-kontroller", "tgcm.eu/Kontroller/old");
}