-
-
Notifications
You must be signed in to change notification settings - Fork 562
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
Plugins: Finalize support for tuist edit #2642
Conversation
ef70b0c
to
c9a896e
Compare
let config = try configLoader.loadConfig(path: path) | ||
return try pluginService.loadPlugins(using: config) | ||
} catch { | ||
logger.warning("Failed to load plugins, attempt to fix the Config manifest and rerun the command. Continuing...") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think here we should differentiate two scenarios:
- Plugins don't exist. In that case, we shouldn't print anything and just ignore them.
- The plugins fail to load. In this case, I'd expect the command to fail with a clear message on why it failed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@pepibumur
Plugins don't exist. In that case, we shouldn't print anything and just ignore them.
This is the behavior currently, so either the config doesn't exist, or it does. In either case loadConfig
will return a default config or the actual config. From here we can get the plugins, if the are no plugins, this does not warn the user and we simply continue.
The plugins fail to load. In this case, I'd expect the command to fail with a clear message on why it failed.
Are we sure we want to fail here? My understanding is we should allow the usage of tuist edit
under most contexts, even if the project may be broken (since they may be using tuist edit
to fix said failure). For context, this warning would be thrown if we are unable to compile either the Config.swift
or any of the Plugin.swift
manifests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed this up a bit, left a more detailed comment below
|
||
/// Attempts to build the loaded plugins. If it fails, shows a warning to the user. | ||
/// - Returns: The built plugin helper modules. | ||
private func buildPluginModules( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I need some context here. Why are we building the plugins for editing the project?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure! So for context we need to build and link plugins that are remote or that don't exist locally.
Let's break it down further:
- If its a remote plugin, we first fetch it from git. From here we checkout to a tag or branch. We don't want to allow users to edit these files since they're in a cache directory and may be wiped at any time. To stop this we instead build the remote plugin and link it so that users can import it in their project.
- If its a local plugin that exists outside of the directory where
tuist edit
was called, then this plugin wont be loaded as part of the xcode project we create for editing plugins (since we only grab plugins that are found underneath the current edit directory). In this case we take this local plugin and build it and link it as we do with the remote one. Again, this use case is only for local plugins that are in some other directory on the machine. For example:If we ran- root -- MyPlugin -- Plugin.swift -- MyProject -- Project.swift
tuist edit
from insideMyProject
then we build and linkMyPlugin
, if instead we rantuist edit
from withinroot
wed haveMyPlugin
as an editable target in the generated edit workspace so we can instead include it as a target (which means you can modify the sources, build, etc)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If its a local plugin that exists outside of the directory where tuist edit was called, then this plugin wont be loaded as part of the xcode project we create for editing plugins (since we only grab plugins that are found underneath the current edit directory).
I wonder if it would maybe make sense to load local plugins into the project.
The use case I am thinking about is the following:
- I have a project with some helpers and I want to extract them to a plugin. To do that I'd appreciate if I could simultaneously edit the plugin and the project I am writing the plugins for.
To me this is the same as for remote x local Swift packages. You are not able to edit remote packages but you are able to edit the local ones.
Also, if I understand it correctly, we want to pre-build remote plugins because their sources location is something we don't want to expose - which is not the case for local plugins, though.
Would it make sense to you @luispadron to change this and make local plugins, even if they are in a different directory, editable and not pre-build them?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah absolutely! I mostly didn't implement it originally because I thought it would be more complex but I have been able to simplify the logic.
So now we load both local plugins to the tuist edit
command as well as any plugins that is on the current file system into the current Plugin.xcodeproj
as a target.
One difference still remains: When editing with a broken config manifest that is in the directory/subdirectory of where the tuist edit
command was run we are able to still load the plugin as an editable target in the project, the same is not true for local plugins outside of this execution path, since we aren't able to determine where they are located.
I'm leaving a general comment in regards to the approach to error handling. Catching errors and printing warnings doesn't align well with how we've been doing error handling. For every |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great @luispadron. I have a question in regards to why we need to build the plugins for the editable project.
I agree with this approach! I think that the In particular, here, I think we need to decide what to do. Should we just fail execution if the However, I'm curious to see what you all think. Its a tricky problem for sure |
b79601b
to
f446eee
Compare
I've updated the error handling to be more inline with what has been done before (i.e. exit with error when issues are caught). The flow:
I believe this way we allow users to leverage Xcode to fix any broken manifests as would be expected but we inform them that since we weren't able to load the config or plugins, features like plugins will not be available (i.e. cannot import plugin module). Let me know what you think ππΌ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking great @luispadron!
Apart from some smaller comments I have made, I'd also reconsider the output of cloning remote plugins.
I think we should hide (when not running with --verbose
) the additional git output and just print something like Downloading RemotePlugin revision ...
.
Otherwise, the feature works seamlessly, exciting times π
plugins: Plugins | ||
) throws -> [ProjectDescriptionHelpersModule] { | ||
let loadedPluginHelpers = plugins.projectDescriptionHelpers.filter { loadedHelper in | ||
!editablePluginManifestPaths.contains(where: { $0.parentDirectory == loadedHelper.path.parentDirectory }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as mentioned above, I'd change this logic to filter only remote plugins and integrate all the local plugins to the editable project directly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Had to modify the way PluginService
returns the loaded plugins since we had previously lost this information (local vs. remote) during translation from description to graph model, but as you mentioned this is worth it so we can support editing all local plugins regardless of their location (as long as they can be read from Config.swift)
@@ -32,37 +33,37 @@ final class ProjectEditorMapper: ProjectEditorMapping { | |||
configPath: AbsolutePath?, | |||
dependenciesPath: AbsolutePath?, | |||
projectManifests: [AbsolutePath], | |||
pluginManifests: [AbsolutePath], | |||
editablePluginManifests: [(String, AbsolutePath)], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I'd change this to either be a new struct or at least name the tuple values as this decreases the final readability of the code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added EditablePluginManifest
private static var temporaryDirectory: AbsolutePath? | ||
private func loadPlugins(at path: AbsolutePath) -> Plugins { | ||
guard let config = try? configLoader.loadConfig(path: path) else { | ||
logger.warning("Unable to load config manifest, fix and re-run to enable extra functionality.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we could make this a little more seamless.
How about changing the message and logic in the following way:
If we stumble upon issues with loading Config.swift
, we could postpone showing this warning and instead of the final message Opening Xcode to edit the project. Press CTRL + C once you are done editing
we could show the user with a warning color a message: Opening Xcode to edit the project. Plugins could not have been loaded because Config.swift could not be compiled, fix compiler errors and press ENTER
. On Enter
we would delete the temporary directory and regenerate the edit project.
This might be a little more complicated to implement, though, since you'd need to pass this information to ProjectEditor
, so we can keep it as an improvement for next PR if you agree this would make sense.
For now, we could just make the warning message a little more explicit, eg. Unable to load Config.swift, fix any compiler errors and re-run for plugins to be loaded.
I think config manifest
is not explicit enough (since manifest nomenclature is something we are familiar with internally but does not have to be immediately obvious to a new user). Plus it's not obvious what's wrong with Config.swift
-> we know, though, it's because we are not able to compile it (right?), so we can add that information.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Totally agree! I think this makes sense for a follow up for sure! I wonder how difficult it would be to just listen to changes in the Config.swift
or Plugin.swift
and reload the project accordingly? I'll look into it as a follow up! ππΌ
@fortmarek Good callout about the logging! I've updated to use verbose flag, also took the time to update how we output this information, opting to use |
bfbb530
to
4442314
Compare
4442314
to
5eca11b
Compare
@@ -79,6 +79,12 @@ jobs: | |||
xcode: ['12.3'] | |||
feature: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorted and added edit
This looks great @luispadron π. I'll go ahead and merge the PR. |
* main: Rebuild frameworks Remove unnecessary step Add Luis to the core team Fix Xcode version Fix syntax issue Bump gatsby-plugin-mdx from 1.10.0 to 2.0.1 in /next (tuist#2652) Plugins: Finalize support for tuist edit (tuist#2642) Drop support for Xcode 11.x (tuist#2651) docs: add tiarnann as a contributor (tuist#2657) Install script bug fix: Adding bin folder to usr/local/ when it is missing (tuist#2655) # Conflicts: # CHANGELOG.md
Resolves #2455
Short description π
This PR finalizes the work needed to allow
tuist edit
to work with projects that use and import plugins.Demo
https://share.getcloudapp.com/qGuXLxN0
Checklist β
CHANGELOG.md
has been updated to reflect the changes. In case of a breaking change, it's been flagged as such.