Skip to content

Conversation

@dcalhoun
Copy link
Member

@dcalhoun dcalhoun commented Apr 22, 2024

Related to https://github.com/Automattic/dotcom-forge/issues/6590.

Proposed Changes

  • Hoist site creation error handling from the remote method to the renderer to ensure all errors originating from the remote method are caught and reported so that we may monitor escalations.
  • Use a generic, user-friendly error message during site creation to avoid displaying a highly technical error message to users.

Testing Instructions

Create site from populated, non-WordPress directory

  1. Add a site via onboarding or the add site modal.
  2. Select an empty directory.
  3. Open your file browser and place a folder/file in the directory.
  4. Continue the site creation.
  5. Verify a user-friendly message is displayed.

Reset a custom local path to the proposed path

  1. Add a site via onboarding or the add site modal.
  2. Select a non-empty, non-WordPress directory.
  3. Verify a user-friendly error message is displayed.
  4. Select the default site directory path (e.g., ~/Studio on macOS).
  5. Modify the site name by adding words.
  6. Verify the local path is set to the proposed path with modifications included, and continuing site creations succeeds.
Before After
site-creation-before site-creation-after

Pre-merge Checklist

  • Have you checked for TypeScript, React or other console errors?

@dcalhoun dcalhoun added the bug label Apr 22, 2024
@dcalhoun dcalhoun self-assigned this Apr 22, 2024
dcalhoun added 11 commits April 22, 2024 10:26
Basedd on the surrounding code, the lack of a return appears to be an
oversight. Introducing the return means creating a site in a non-empty,
non-WordPress directory no longer displays an error, and instead
dismisses the modal after moving the directory's content to the OS
trash. The apparent rationale for doing so is allowing the user to
retry with a clean directory.

A future improvement would be communicating the non-empty state to the
user, allowing them to take whatever action they deem appropriate.
The flex layout caused the element to shrink.
The error messages provided from the remote process can be highly
technical and unhelpful.

To start, they are all prefixed with confusing technical details:

> Error invoking remote method <name>

Also, the error origin can be very technical itself, e.g.:

> Error: ENOENT: no such file or directory, scandir '/Users/ajayghaghretiya/Library/Application Support/Studio/server-files/wordpress-versions/latest/wp-content/themes/twentytwentythree/parts'

To improve the user experience, we provide a generic, user-friendly
message instead, regardless of the origin.
Site creation silently failing is confusing. Instead, we should
communicate the error and ask that they retry.
Ensure we can monitor escalation of this type of error.
Site creation silently failing is confusing. Instead, we should
communicate the error and ask that they retry.
This is no longer presented to the end-user. We can instead rely upon
the original error message.
Surrounding logic does not rely upon the `err` abbreviation.
Hoisting the `try`/`catch` to the main process invocation site ensures
that all errors originating from logic within the main process method
are caught. Previously, only certain portions of the logic were wrapped
in `try`/`catch`, meaning some errors would go unreported.
Allow selecting the default site path (e.g., `~/Studio` on macOS) as a
means of resetting prior path selections. This allows users to recover
from mistakenly changing the local path to a non-empty, non-WordPress
directory.
@dcalhoun dcalhoun force-pushed the fix/improve-site-creation-error-handling branch from 11397ce to e3efda7 Compare April 22, 2024 14:27
@dcalhoun dcalhoun marked this pull request as ready for review April 22, 2024 15:08
@dcalhoun dcalhoun requested review from a team, kozer and sejas April 22, 2024 15:11
>
<Icon
className={ error ? 'fill-red-500' : '' }
className={ cx( 'shrink-0 basis-4', error ? 'fill-red-500' : '' ) }
Copy link
Member Author

Choose a reason for hiding this comment

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

Prevent form validation message icons from shrinking due to sibling content.

setSitePath( path );
const pathResetToDefaultSitePath =
path === proposedSitePath.substring( 0, proposedSitePath.lastIndexOf( '/' ) );
setSitePath( pathResetToDefaultSitePath ? '' : path );
Copy link
Member Author

Choose a reason for hiding this comment

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

Allow users to reset the path selection value by selecting the default Studio directory. The outcome is using the currently proposed path instead, including any future changes to the site name.

await createSite( path, siteName ?? '' );
} catch ( e ) {
setError( ( e as Error )?.message );
Sentry.captureException( e );
Copy link
Member Author

Choose a reason for hiding this comment

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

Error reporting was hoisted here from src/ipc-handlers.ts to ensure all errors originating from the remote createSite method were caught. Previously, portions of the remote method could result in displaying error messages, but not reporting them to Sentry — e.g, the error shared in https://github.com/Automattic/dotcom-forge/issues/6590 — as we did not wrap all logic in createSite with try/catch blocks.

Comment on lines +60 to +64
setError(
__(
'An error occurred while creating the site. Verify your selected local path is an empty directory or an existing WordPress folder and try again. If this problem persists, please contact support.'
)
);
Copy link
Member Author

Choose a reason for hiding this comment

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

A generic error message here replaces displaying the previous error messages thrown from src/ipc-handlers.ts. This was done for a few reasons:

  1. All error messages originating from remote methods are prefixed with a non-user-friendly message of Error invoking remote method <method-name>. We could remove this with regular expression, but...
  2. Error messages from remote messages could originate from numerous failures, including references to highly technical details as outlined in Automattic/dotcom-forge#6590. It is challenging to filter out these "unknown" errors aside from parsing the error message string because...
  3. Electron does not provide an ability to communicate custom errors across the bridge (quoted below) aside from the message itself. Parsing the message to determine known vs unknown errors feels brittle.

Errors thrown through handle in the main process are not transparent as they are serialized and only the message property from the original error is provided to the renderer process. Please refer to #24427 for details.

Comment on lines 121 to -127
if ( ! ( await isEmptyDir( path ) ) && ! isWordPressDirectory( path ) ) {
wasPathEmpty = true;
Copy link
Member Author

Choose a reason for hiding this comment

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

The combination of ! ( await isEmptyDir( path ) ) and setting wasPathEmpty = true; appears contradictory. Additionally, now that all error handling is hoisted to the renderer, I removed this assignment and related logic.

@sejas are you able to share the original intent behind this logic (when and why it should run) and whether its removal will cause problems? 🙇🏻

I shared additional thoughts in https://github.com/Automattic/local-environment/pull/135#discussion_r1571371185.

Copy link
Member

Choose a reason for hiding this comment

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

I agree that it is contradictory.
The intention was when creating a site failed, we should remove all the new files created to allow the user to recreate it again with the same folder name.


if ( ! ( await isEmptyDir( path ) ) && ! isWordPressDirectory( path ) ) {
wasPathEmpty = true;
userData.sites;
Copy link
Member Author

Choose a reason for hiding this comment

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

@kozer I noted this one reference to userData.sites did not have a return statement like all others in this method. I presumed this was oversight.

Are you able to share the original intent of returning userData.sites? Will this removal cause issue? 🙇🏻

I shared additional thoughts in https://github.com/Automattic/local-environment/pull/55#discussion_r1571371113.

Copy link
Member

@sejas sejas left a comment

Choose a reason for hiding this comment

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

Tested both flows where I added a folder inside the empty folder and I received the error message 👌
After selecting Studio, the name selection worked fine.

Thanks for this PR, this kind of PRs are my favorite. Removing code and simplifying the logic. Congrats!

I think the cases were the process of creating a site could fail were reduced almost to zero, so I think we don't need the wasEmpty logic anymore.

creating-site.mp4

Comment on lines -167 to -170
if ( wasPathEmpty ) {
// Clean the path to let the user try again
await shell.trashItem( path );
}
Copy link
Member

@sejas sejas Apr 23, 2024

Choose a reason for hiding this comment

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

Here is where we removed all the files after a failed site creation in an empty directory.
I'm seeing you have removed the try/catch so I guess we assume the site creation is not likely to fail.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm seeing you have removed the try/catch so I guess we assume the site creation is not likely to fail.

No, not necessarily. I believe site creation could still fail, the primary focus of the proposed changes is hoisting error handling higher in the stack so that all errors that might occur in createSite method are caught.

However, as you noted, I also removed the clean up logic as I am not confident it functions as expected.

Currently, I'm perplexed by the original logic, which is why I inquired about its original intent in #4 (comment). It could be that we need to retain this logic — I'm not sure. I'd like your personal perspective on the logic's intent/necessity and the overall stability of this new site creation logic.

Here is where we removed all the files after a failed site creation in an empty directory.

Correct. I am not sure when this logic would actually execute. We set wasPathEmpty = true; when the directory is a non-empty, non-WP directory, but then only run the clean up logic when the path is a WP directory. That appears contradictory to me and I'm not sure in what circumstance that situation would be possible.

I think the cases were the process of creating a site could fail were reduced almost to zero, so I think we don't need the wasEmpty logic anymore.

Copied this quote from your top-level review comment as I felt it related to this specific discussion as well. I'm not confident it will never fail, but my hope is that we will at least catch any errors that occur and provide a helpful error message.

Thank you for helping me understand this logic! It's definitely an important piece of functionality. 🙇🏻

Copy link
Member Author

Choose a reason for hiding this comment

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

Noting that a recent report (ref: pdKhl6-3bi-p2#comment-6383) suggests site creation can fail leaving a partial WP directory installation. My perception is that the clean up logic intended to help rectify such a situation. However, as I mentioned in #4 (comment), I lack confidence that it works as expected and I wonder if we should prompt for user permission before taking this action, as removing a directory might inadvertently discard valuable user files.

Copy link
Member Author

Choose a reason for hiding this comment

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

@sejas following up to see I you have additional insight on the findings I expressed in my previous comments.

Completely fine if we need more time to review this. I'd prefer to understand the prior logic's intention before removing it — I'd hate to introduce bugs or make the UX less stable.

Thanks in advance for the help! 🙇🏻

Copy link
Member

@sejas sejas Apr 26, 2024

Choose a reason for hiding this comment

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

@dcalhoun, Ideally we could keep the logic of deleting the site if the creation fails.

I wonder if we should prompt for user permission before taking this action, as removing a directory might inadvertently discard valuable user files.

That's why I introduced the wasEmpty flag. We should wrap it around a correct if statement that correctly checks if the given folder is empty. Then we can assume the user doesn't have any "custom" files in that folder, and all the files in that folder are created by our app. Also note that the app moves that folder into the trash and don't really remove it from the disk.

Copy link
Member Author

@dcalhoun dcalhoun Apr 26, 2024

Choose a reason for hiding this comment

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

@sejas I interpret your latest comment to mean the logic's intention was to clean up only app-generated files after failed attempts to create a new site in an empty directory, but that I am correct in that it currently does not function as intended because of the conflicting conditionals I referenced in #4 (comment). You believe it is ideal to retain the clean up logic, provided we fix the currently broken state.

Is all of that interpretation accurate? If so, I will work to reinstate the clean up logic.

Thanks for your patience, I repeat all this back to avoid any misunderstandings.

Copy link
Member

Choose a reason for hiding this comment

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

Yes!, all that is correct.

We can also re-introduce the correct condition, "clean up the directory when the create fails and all the files were created by Studio", in a separate PR if you prefer that.

Copy link
Member Author

Choose a reason for hiding this comment

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

Merged these changes as-is and created #68 to follow up on this specific discussion. Thanks!

@dcalhoun dcalhoun merged commit f4632cf into trunk Apr 30, 2024
@dcalhoun dcalhoun deleted the fix/improve-site-creation-error-handling branch April 30, 2024 13:55
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.

3 participants