Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Theia data folder name configurable #6650

Closed
wants to merge 8 commits into from
Closed

Make Theia data folder name configurable #6650

wants to merge 8 commits into from

Conversation

mmorhun
Copy link
Contributor

@mmorhun mmorhun commented Nov 27, 2019

Signed-off-by: Mykola Morhun mmorhun@redhat.com

What it does

Makes Theia data folder configurable, so it could be redefined to another than .theia.

Resolves #4488

Review checklist

Reminder for reviewers

@mmorhun mmorhun added the plug-in system issues related to the plug-in system label Nov 27, 2019
@mmorhun mmorhun self-assigned this Nov 27, 2019
@akosyakov
Copy link
Member

akosyakov commented Nov 28, 2019

The point is to use UserStorage interfaces, not constants, i.e. if a user storage is reimplemented with the database instead of filesystem. Not sure how good it matches to VS Code APIs. Do they expose fs paths to storages?

@akosyakov akosyakov added the user storage issues related to user storage label Nov 28, 2019
@mmorhun
Copy link
Contributor Author

mmorhun commented Nov 28, 2019

Yes they do. For example here.
Also to me it looks weird to call frontend from backed to call back backend which uses the same implementation... (and we cannot have all things on frontend). So I would prefer to stay with current logic at the moment (but yes, contracts like .theia should be aligned).

@akosyakov
Copy link
Member

So I would prefer to stay with current logic at the moment (but yes, contracts like .theia should be aligned).

But they are already aligned it, i.e. .theia in the user storage and the plugin system. My concern was that if someone change the implementation to use another folder or completely to another data storage. Constant won't help, the plugin system will keep using .theia.

@mmorhun
Copy link
Contributor Author

mmorhun commented Nov 29, 2019

My concern was that if someone change the implementation to use another folder or completely to another data storage. Constant won't help, the plugin system will keep using .theia

I think, the case with using of another folder is handled by using the constant.
As for complete changing of data storage mechanism, yes plugin system will continue using its own implementation. From one point of view it might be not that good, but if we look at it from another side it's not that bad. For example, the specification doesn't limit the size of data a plugin/extension could store in there. That means, it might save its cache there which could be quite a lot. And if we use the same storage (under completely another data storage you probably meant database, right?) for Theia and plugin data it may affect performance of main Theia which shouldn't happen.

@akosyakov
Copy link
Member

As for complete changing of data storage mechanism, yes plugin system will continue using its own implementation.

Then it does not address #4488 If i rename .theia to .che it is not going to work. There should be only one place where it is configured in Theia, all parts should respect it. The user storage is the place. The plugin system must use it.

And if we use the same storage (under completely another data storage you probably meant database, right?) for Theia and plugin data it may affect performance of main Theia which shouldn't happen.

We should care about the default implementation. Performance issues of custom user data storage it is extenders' problems.

@mmorhun
Copy link
Contributor Author

mmorhun commented Nov 29, 2019

If i rename .theia to .che it is not going to work.

It will work, just Theia will use another folder for its data (which means previous settings are unused).

@mmorhun
Copy link
Contributor Author

mmorhun commented Nov 29, 2019

There should be only one place where it is configured in Theia

Agree. It should be possible. As for now, such an option is the hardcoded constant. So is it ok for you to have rebindable value for this constant to resolve the issue?

@mmorhun
Copy link
Contributor Author

mmorhun commented Dec 4, 2019

@akosyakov it would be nice to have some agreement on this issue.
I understand your point about using user storage as single point, but I have concerns about such approach:

  1. To use user storage service of Theia we should perform all the invocations to it from frontend part of Theia, as the service doesn't have backend part at all. However, dealing with file system is pure backend task, which means we would need to transfer data to frontend and then the user storage service will transfer it again to backend. Also, in case of using user storage service we cannot send only diffs, but would be required to send all data in the key-value storage on any update. It could be a hundred of bytes as well as a hundred of megabytes. Is it ok to send, say, 10 Mb of data to frontend and back on a, for example, 2 Kb update?

  2. Key-value storage in plugin API implementation don't have to use default implementation of user storage service. One may want to use another solution (taking into account key-value nature of data), say etcd. How is it possible in case of using user storage service?

@lmcbout
Copy link
Contributor

lmcbout commented Dec 4, 2019

I might be missing something here, but moving the constant "PluginPaths.THEIA_DIR" to "THEIA_USER_STORAGE_FOLDER" does not change anything
I don't use window environment, but on UNIX environment we can see a hidden folder for ".vscode", ".theia", ".eclipse", ".go" ... under the user home folder. Each environment/program has its own hidden folder.
If we want to modify the "/.theia", why not using a preference, so it could use the "user folder" from other tools folder. As an example, we could define the "Theia user home to point to "/.vscode folder". As default, we keep the "THEIA_USER_STORAGE_FOLDER" pointing to "/.theia" in Unix environment and we can have a default for window environment !

@akosyakov
Copy link
Member

akosyakov commented Dec 6, 2019

Agree. It should be possible. As for now, such an option is the hardcoded constant. So is it ok for you to have rebindable value for this constant to resolve the issue?

Let's improve our EnvVariablesServer instead since it is in core and in node but also exposed as JSON-RPC service, it can be used by all extensions on frontend and backend. We need then following methods:

  • getDataFolderName, i.e. .theia (used by all extensions)
  • getUserDataPath, i.e. path.join(os.userhome(), dataFolderName) (used by the user storage and other extensions)
  • getAppDataPath, i.e. instead of PluginPathsServiceImpl.getTheiaDirPath, though should rely on dataFolderName. It should be used by the plugin system extension for now only. As far as I understand the difference that these data can be shared between different PCs for the same user in the same domain. That's a complete implementation in VS Code: https://github.com/microsoft/vscode/blob/0fae04b8a263cc5b8e8ec4c6a286bb57ae39d6c0/src/paths.js#L18-L26 I don't really understand why it was introduced in Theia and not completely, but only for Windows. Let's keep it if nobody knows for now.

We should review the code in Theia repo where we use .theia as a folder name or part of user data or app path and then use EnvVariablesServer instead. Concrete products can overwrite EnvVariablesServer.getDataFolderName. Later we can allow to configure it via the application package as well. It should not be a preference, since a user should not be able to change it.

@mmorhun
Copy link
Contributor Author

mmorhun commented Dec 17, 2019

I've updated the PR according to our discussion above.
Despite I tested it manually, I would like someone to test it too just for case if I missed something.

@mmorhun mmorhun changed the title Use constant instead of '.theia' in PluginPathsService Make Theia data folder name configurable Dec 17, 2019
@akosyakov
Copy link
Member

akosyakov commented Dec 17, 2019

@mmorhun that's really good changes to enable installing different theia products next to each other! Please look at comment though, it would be good to make it less breaking in some places.

@elaihau @RomanNikitenko please check whether breaking in the task extension can cause any issue, at first look it is fine

@elaihau
Copy link
Contributor

elaihau commented Dec 19, 2019

@elaihau @RomanNikitenko please check whether breaking in the task extension can cause any issue, at first look it is fine

The impacted feature that I can think of is Run build / test task.

I tested in the example browser. Run build / test task works propertly with this change.

Signed-off-by: Mykola Morhun <mmorhun@redhat.com>
@mmorhun
Copy link
Contributor Author

mmorhun commented Feb 21, 2020

The PR is ready to be tested again.

Copy link
Contributor

@RomanNikitenko RomanNikitenko left a comment

Choose a reason for hiding this comment

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

@mmorhun
I tested the changes after your update and found that unfortunately the plugins can not be started for me...
I tested your changes as is, without overriding .theia folder.
Could you check it, can you reproduce it?

thanks in advance!

error_starting_workspace

Copy link
Contributor

@kittaakos kittaakos left a comment

Choose a reason for hiding this comment

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

I am going to try it on Windows...

Comment on lines +78 to +79
async createUri(folder: URI, configPath: string, configName: string = this.getConfigName()): Promise<URI> {
if (!configPath) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This API change is odd.
Let's put aside, it was sync and now it is async, but earlier I could do

pc.createUri(myFolderUri)

Now I have to do

pc.createUri(myFolderUri, '') // Note the empty string

You ignored the default values. I guess we cannot avoid a breaking API change, then let's have a better API. Thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

To avoid passing of empty string we may default the argument to it. Just missed the usage. My PR changes many things...

dataFolderUriBuilder.resolve(WINDOWS_APP_DATA_DIR);
dataFolderUriBuilder.resolve(WINDOWS_ROAMING_DIR);
}
dataFolderUriBuilder.resolve(await this.getDataFolderName());
Copy link
Contributor

Choose a reason for hiding this comment

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

Same as above, please check and tell me if I have overlooked something.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You are right, will fix

}

async getUserDataFolder(): Promise<string> {
return FileUri.create(await this.getUserHomeFolder()).resolve(await this.getDataFolderName()).toString();
Copy link
Contributor

Choose a reason for hiding this comment

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

The two awaits do not depend on each other:

    async getUserDataFolder(): Promise<string> {
        const [home, data] = await Promise.all([
            this.getUserHomeFolder(),
            this.getDataFolderName()
        ]);
        return FileUri.create(home).resolve(data).toString();
    }

getDataFolderName(): Promise<string>
getUserDataFolder(): Promise<string>
/** Windows specific. Returns system data folder of Theia. On other than Windows systems is the same as getUserDataFolder */
getAppDataFolder(): Promise<string>
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we need the app data folder? Is it required to comply with VS Code APIs?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

On Windows VSCode uses AppData folder for some info.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, it is for VS Code compatibility, but we should understand it is better. I don't think it is something Windows or VS Code specific.

For instance, Electron distinguishes between the app folder (app.getPath('appData')) and the user folder (app.getPath('userData')), see https://github.com/electron/electron/blob/master/docs/api/app.md#appgetpathname. If I understand correctly former is used for place where program stores its files and second one where a program stores user files. If it is true, we should put it like that and list examples of files in comments.

@thegecko @mcgordonite Maybe you can shed some light on this question :) I think this PR is important for you, it is cleanly separates how to configure user folder for the concrete product.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, it is for VS Code compatibility

Can you please link the corresponding VS Code source? I couldn't find it with a text search for getAppDataFolder in the vscode repo.

Copy link
Member

Choose a reason for hiding this comment

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

userData folder is simply the appData folder with the current application name at the end. It's not Windows specific.

e.g. on MacOS:

appData: ~/Library/Application Support
userData: ~/Library/Application Support/Mbed Studio (for example)

Copy link
Member

@akosyakov akosyakov Feb 24, 2020

Choose a reason for hiding this comment

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

I'm thinking that we use userData here bogusly. We probably should name it differently, user data it like where local storage (app state specific to user) is stored, not there settings or recent workspaces are stored.

Maybe we should call it appHome folder instead to avoid confusions with userData meaning from Electron? So there would be 4 folders:

  • userHome: ~
  • appHome = user home + home folder name: ~/.theia
  • appData: ~/Library/Application Support
  • userData = appData + productName: ~/Library/Application Support/Mbed Studio (for example)

Does it make sense?

Copy link
Member

Choose a reason for hiding this comment

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

@mmorhun I'm a bit confused, I could not find a single client making use of getAppDataFolder. Did I overlook something?

Copy link
Contributor

Choose a reason for hiding this comment

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

I could not find a single client making use of getAppDataFolder

That's why I have asked: why do we need it.

Copy link
Member

@akosyakov akosyakov Feb 24, 2020

Choose a reason for hiding this comment

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

let's keep a naming as it's and add js-docs:

  • userDataFolder controls a folder where user data like settings, recent workspaces and plugins are stored
  • appDataFolder controls a folder where app data are stored, like plugin logs and storages

btw PluginPathsServiceImpl.getLogsDirPath and PluginPathsServiceImpl.getWorkspaceStorageDirPath are bogus, they should use getAppDataFolder, not getUserDataFolder

Also getAppDataFolder implementation should be aligned with https://github.com/microsoft/vscode/blob/f3b191ab38fb9f1717ce5ce3396bb41204ffb399/src/paths.js#L18-L25, except bits of VSCODE_DATA, APP_DATA is Electron variable and USERPROFILE is windows variable, using homedir always is bogus

Copy link
Member

Choose a reason for hiding this comment

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

I've not seen it used, but you could imagine a company with multiple apps potentially sharing config data between them there?
I would be inclined to keep it and name it how an electron user would expect to see it.

Signed-off-by: Mykola Morhun <mmorhun@redhat.com>
import { PLUGIN_RPC_CONTEXT, EnvMain } from '../common';
import { EnvVariablesServer, EnvVariable } from '@theia/core/lib/common/env-variables';

export class EnvVariablesServerExt implements EnvVariablesServer {
Copy link
Member

Choose a reason for hiding this comment

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

Why does in plugin code needs to implement theia core apI? I see that many new methods are not used to implement theia.d.ts. Please remove it does not help supporting VS Code APIs.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We've added some new methods into Theia APIs over VSCode APIs. They were required for some Theia plugins features. Does having extra APIs hurt?
As about my changes. Yes, I added new methods which are not used in theia.d.ts implementation, but they are needed to comply with EnvVariablesServer interface. I believe, that refactoring of this is out of scope for this PR.

Copy link
Member

@akosyakov akosyakov Feb 24, 2020

Choose a reason for hiding this comment

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

We have conventions that main and ext shapes should be defined in the plugin api protocol file, here we don't follow it but trying to reuse code from core which can pull core deps in plugin host process, in additional we add methods which are not needed. cc @benoitf @tsmaeder

i.e. there should be EnvExt interface which only has methods required to implement EnvMain. core APIs should not be used in the plugin host process. Then it will be consistent with design of all other plugin shapes without any unnecessary code.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@akosyakov I completely agree with you. What I am telling is that such refactoring is out of scope of this PR. I prefer to have a PR which fixes one specific issue. Otherwise we will have a mess in PR, long terms PRs and the process of contributing is getting too long.

Copy link
Member

@akosyakov akosyakov Feb 24, 2020

Choose a reason for hiding this comment

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

I think such refactoring makes the PR smaller, since you remove a lot of code instead of adding new code trying to save the bogus approach. It will be out of scope if you won't change this file or protocol, but you did already.

@@ -992,7 +993,13 @@ export interface DocumentsMain {

export interface EnvMain {
$getEnvVariable(envVarName: string): Promise<string | undefined>;
$getAllEnvVariables(): Promise<EnvVariable[]>
Copy link
Member

Choose a reason for hiding this comment

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

I am not sure why new APIs are necessary. Do you implement some VS Code API with them?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Probably I changed formatting or so for this line. It was added long time ago.

Copy link
Member

@akosyakov akosyakov Feb 24, 2020

Choose a reason for hiding this comment

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

Not sure what you mean, there are no such APIs on master

export interface EnvMain {
$getEnvVariable(envVarName: string): Promise<string | undefined>;
$getClientOperatingSystem(): Promise<theia.OperatingSystem>;
}
and there is no formatting and whitespace changes

Copy link
Contributor Author

@mmorhun mmorhun Feb 24, 2020

Choose a reason for hiding this comment

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

My mistake, sorry.

const globalStorageFolderPath = new Path(userDataFolderPath).join('globalStorage').toString();

// Make sure that folder by the path exists
if (! await this.fileSystem.exists(globalStorageFolderPath)) {
Copy link
Member

@akosyakov akosyakov Feb 24, 2020

Choose a reason for hiding this comment

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

remote Theia APIs accept URIs, not paths

@mmorhun
Copy link
Contributor Author

mmorhun commented Feb 24, 2020

@RomanNikitenko thanks for testing. When I tested this PR a few days ago it was working with a plugin. When I tested it today, I had the same error you had... weird...
The problem was in FileUri.fsPath, it was returning wrong path: /file:/right/path. I've already committed the quick fix.

@@ -450,7 +454,7 @@ export class PreferencesEditorsContainer extends DockPanel {

let uri = preferenceUri;
if (preferenceUri.scheme === UserStorageUri.SCHEME && homeUri) {
uri = homeUri.resolve(THEIA_USER_STORAGE_FOLDER).resolve(preferenceUri.path);
uri = homeUri.resolve(await this.envServer.getDataFolderName()).resolve(preferenceUri.path);
Copy link
Member

@akosyakov akosyakov Feb 24, 2020

Choose a reason for hiding this comment

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

we should get rid of homeUri here and use getUserDataFolder instead, homeUri breaks abstraction by assuming that user home is this.fileSystem.getCurrentUserHome in EnvVariableServer

Copy link
Member

Choose a reason for hiding this comment

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

I wonder should we deprecate FileSystem.getCurrentUserHome in favour of EnvVariableServer.getUserHomeFolder. I think it would be more correct conceptually and prohibit breaking the abstraction as above.

cc @kittaakos @svenefftinge

Copy link
Member

@akosyakov akosyakov Feb 24, 2020

Choose a reason for hiding this comment

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

let's deprecate, it does not make sense to have 2 APIs for the same thing. No need to change clients right now, just add @deprecate annotation which says that EnvVariableServer.getUserHomeFolder should be used instead

@injectable()
export class UserStorageServiceFilesystemImpl implements UserStorageService {

protected readonly toDispose = new DisposableCollection();
protected readonly onUserStorageChangedEmitter = new Emitter<UserStorageChangeEvent>();
protected readonly userStorageFolder: Promise<URI | undefined>;
Copy link
Member

Choose a reason for hiding this comment

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

this line should be reverted, readonly is correct, variable is assigned in the constructor

@mmorhun
Copy link
Contributor Author

mmorhun commented Feb 24, 2020

Guys, I'm not sure that I will be able to address all your comments before release. I had well tested solution, but all these new remarks require some additional time that I don't have at the moment...

@kittaakos
Copy link
Contributor

kittaakos commented Feb 24, 2020

Thanks for the heads-up, @mmorhun. No worries, you have already taken care of the significant part of this task 👍, do you want me to continue on the top of your work? Give me the green lights, and I can assist you a bit.

@akosyakov
Copy link
Member

@mmorhun thank you for all great job on this PR anyway! It is fine to collaborate and work together to get stuff done. I think it would be fine if @kittaakos push an additional commit to address remaining comments. As usual authorship of the original commit should be preserved: https://github.com/eclipse-theia/theia/blob/master/doc/pull-requests.md#completing-pr

@mmorhun
Copy link
Contributor Author

mmorhun commented Feb 24, 2020

Thank you guys for good review and spotting issue in this PR. I don't mind if some of you continue this work. Thank you!

@akosyakov
Copy link
Member

@mmorhun I've noticed that you resolved some conversations. Does it mean you are going to push once more or we can unresolve them?

@kittaakos
Copy link
Contributor

kittaakos commented Feb 24, 2020

To sum up:

  • we want to have one single method which returns with the appDataFolder. (We do not want to support userHome and appDataFolder separately),
  • we will use this appDataFolder to store user preferences, keybindings, and recent workspaces,
  • appDataFolder location is not used for the VS Code extensions, they should still go under ~/yourAppName (where yourAppName defaults to .theia)
  • we want to mimic what VS Code does here,
  • we want the same behavior for both the browser and electron environments,
  • this will break existing, user-defined preferences (settings.json), keybindings (keymaps.json), and recently opened workspaces (recentworkspace.json).

This is not a solution for non-bundled electron applications, as the application name is always Electron unless you have used electron-builder.

Edit: I have added this item to our forthcoming dev-meeting: https://github.com/eclipse-theia/theia/wiki/Dev-Meetings#agenda-2020-02-18

@kittaakos
Copy link
Contributor

  • this will break existing,

See above 👆

CC @thegecko

@mmorhun
Copy link
Contributor Author

mmorhun commented Feb 24, 2020

@akosyakov my changes are trivial, so no value of pushing them now. But I've unresolved conversations.

@akosyakov
Copy link
Member

akosyakov commented Feb 24, 2020

we want to have one single method which returns with the appDataFolder. (We do not want to support userHome and appDataFolder separately),

@kittaakos We should figure out though why VS Code does not want to unpack extensions under app data folder as well, but always under user home. It would be important for support of VS Code extensions in Electron, i.e. on install they should be unpacked somewhere and loaded on next start from there as well. Not something for this PR, but we should know why at least.

@thegecko
Copy link
Member

this will break existing,

Really not good :(

If it has to be done, we should at least get it in before v1.

@kittaakos
Copy link
Contributor

We should figure out though why VS Code does not want to unpack extensions under app data folder as well,

Here is the answer:

because the paths that you get from getAppDataPath are not for storing extensions, they are for configuration files
at least on Linux they use $XDG_CONFIG_HOME, which is intended for configuration files and not a bunch of extensions

From the XDG Base Directory Specification specs:

There is a single base directory relative to which user-specific data files should be written. This directory is defined by the environment variable $XDG_DATA_HOME.

Where the user-specific data files is the important part. No extensions, just config files.

@akosyakov
Copy link
Member

It's turned out that VS Code strategy does not follow platform guidance and cause various issues: microsoft/vscode#3884 (comment) For them it is hard already to migrate for us it would be good time to learn and make it properly.

@mmorhun
Copy link
Contributor Author

mmorhun commented Feb 25, 2020

@kittaakos JFYI, I use Fedora (31 as of now) but both XDG_CONFIG_HOME and XDG_DATA_HOME are empty. It means that system falls back to its defaults. But here we possibly might have differences across distributions...

@kittaakos
Copy link
Contributor

It was resolved via #7214.
I'm going to close it.

Thanks for the help on this, @mmorhun! 👍

@kittaakos kittaakos closed this Feb 26, 2020
@kittaakos kittaakos deleted the theia-4488 branch February 26, 2020 10:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
plug-in system issues related to the plug-in system user storage issues related to user storage
Projects
None yet
Development

Successfully merging this pull request may close these issues.

plugin system breaks user storage abstraction
8 participants