-
Notifications
You must be signed in to change notification settings - Fork 50
fix: Improve site creation error handling #4
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
Changes from all commits
e969e1b
2092e55
1412f40
e5b4f50
dca992e
690e9f2
1e5e023
b9794de
e3c2a4e
a1ae7f1
e3efda7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| import * as Sentry from '@sentry/electron/renderer'; | ||
| import { useI18n } from '@wordpress/react-i18n'; | ||
| import { useCallback, useMemo, useState } from 'react'; | ||
| import { getIpcApi } from '../lib/get-ipc-api'; | ||
|
|
@@ -28,11 +29,13 @@ export function useAddSite() { | |
| const { path, name, isEmpty, isWordPress } = response; | ||
| setDoesPathContainWordPress( false ); | ||
| setError( '' ); | ||
| setSitePath( path ); | ||
| const pathResetToDefaultSitePath = | ||
| path === proposedSitePath.substring( 0, proposedSitePath.lastIndexOf( '/' ) ); | ||
| setSitePath( pathResetToDefaultSitePath ? '' : path ); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Allow users to reset the path selection value by selecting the default |
||
| if ( siteWithPathAlreadyExists( path ) ) { | ||
| return; | ||
| } | ||
| if ( ! isEmpty && ! isWordPress ) { | ||
| if ( ! isEmpty && ! isWordPress && ! pathResetToDefaultSitePath ) { | ||
| setError( | ||
| __( | ||
| 'This directory is not empty. Please select an empty directory or an existing WordPress folder.' | ||
|
|
@@ -45,20 +48,25 @@ export function useAddSite() { | |
| setSiteName( name ?? null ); | ||
| } | ||
| } | ||
| }, [ __, siteWithPathAlreadyExists, siteName ] ); | ||
| }, [ __, siteWithPathAlreadyExists, siteName, proposedSitePath ] ); | ||
|
|
||
| const handleAddSiteClick = useCallback( async () => { | ||
| setIsAddingSite( true ); | ||
| try { | ||
| const path = sitePath ? sitePath : proposedSitePath; | ||
| await createSite( path, siteName ?? '' ); | ||
| } catch ( e ) { | ||
| setError( ( e as Error )?.message ); | ||
| Sentry.captureException( e ); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Error reporting was hoisted here from |
||
| 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.' | ||
| ) | ||
| ); | ||
|
Comment on lines
+60
to
+64
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
|
||
| setIsAddingSite( false ); | ||
| throw e; | ||
| } | ||
| setIsAddingSite( false ); | ||
| }, [ createSite, proposedSitePath, siteName, sitePath ] ); | ||
| }, [ createSite, proposedSitePath, siteName, sitePath, __ ] ); | ||
|
|
||
| const handleSiteNameChange = useCallback( | ||
| async ( name: string ) => { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -112,20 +112,15 @@ export async function createSite( | |
| ): Promise< SiteDetails[] > { | ||
| const userData = await loadUserData(); | ||
| const forceSetupSqlite = false; | ||
| let wasPathEmpty = false; | ||
| // We only try to create the directory recursively if the user has | ||
| // not selected a path from the dialog (and thus they use the "default" path) | ||
| // We only recursively create the directory if the user has not selected a | ||
| // path from the dialog (and thus they use the "default" or suggested path). | ||
| if ( ! ( await pathExists( path ) ) && path.startsWith( DEFAULT_SITE_PATH ) ) { | ||
| try { | ||
| fs.mkdirSync( path, { recursive: true } ); | ||
| } catch ( err ) { | ||
| return userData.sites; | ||
| } | ||
| fs.mkdirSync( path, { recursive: true } ); | ||
| } | ||
|
|
||
| if ( ! ( await isEmptyDir( path ) ) && ! isWordPressDirectory( path ) ) { | ||
| wasPathEmpty = true; | ||
|
Comment on lines
121
to
-127
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The combination of @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.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree that it is contradictory. |
||
| userData.sites; | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kozer I noted this one reference to Are you able to share the original intent of returning I shared additional thoughts in https://github.com/Automattic/local-environment/pull/55#discussion_r1571371113. |
||
| // Form validation should've prevented a non-empty directory from being selected | ||
| throw new Error( 'The selected directory is not empty nor an existing WordPress site.' ); | ||
| } | ||
|
|
||
| const allPaths = userData?.sites?.map( ( site ) => site.path ) || []; | ||
|
|
@@ -148,27 +143,18 @@ export async function createSite( | |
| const server = SiteServer.create( details ); | ||
|
|
||
| if ( isWordPressDirectory( path ) ) { | ||
| try { | ||
| // If the directory contains a WordPress installation, and user wants to force SQLite | ||
| // integration, let's rename the wp-config.php file to allow WP Now to create a new one | ||
| // and initialize things properly. | ||
| if ( forceSetupSqlite && ( await pathExists( nodePath.join( path, 'wp-config.php' ) ) ) ) { | ||
| fs.renameSync( | ||
| nodePath.join( path, 'wp-config.php' ), | ||
| nodePath.join( path, 'wp-config-studio.php' ) | ||
| ); | ||
| } | ||
| // If the directory contains a WordPress installation, and user wants to force SQLite | ||
| // integration, let's rename the wp-config.php file to allow WP Now to create a new one | ||
| // and initialize things properly. | ||
| if ( forceSetupSqlite && ( await pathExists( nodePath.join( path, 'wp-config.php' ) ) ) ) { | ||
| fs.renameSync( | ||
| nodePath.join( path, 'wp-config.php' ), | ||
| nodePath.join( path, 'wp-config-studio.php' ) | ||
| ); | ||
| } | ||
|
|
||
| if ( ! ( await pathExists( nodePath.join( path, 'wp-config.php' ) ) ) ) { | ||
| await setupSqliteIntegration( path ); | ||
| } | ||
| } catch ( error ) { | ||
| Sentry.captureException( error ); | ||
| if ( wasPathEmpty ) { | ||
| // Clean the path to let the user try again | ||
| await shell.trashItem( path ); | ||
| } | ||
|
Comment on lines
-167
to
-170
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
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 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.
Correct. I am not sure when this logic would actually execute. We set
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. 🙇🏻
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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! 🙇🏻
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
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.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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! |
||
| throw new Error( 'Error creating the site. Please contact support.' ); | ||
| if ( ! ( await pathExists( nodePath.join( path, 'wp-config.php' ) ) ) ) { | ||
| await setupSqliteIntegration( path ); | ||
| } | ||
| } | ||
|
|
||
|
|
||
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.
Prevent form validation message icons from shrinking due to sibling content.