Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/hooks/sync-sites/use-sync-push.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,11 @@ export function useSyncPush( {
status: pushStatesProgressInfo.failed,
} );
getIpcApi().showErrorMessageBox( {
title: sprintf( __( 'Error exporting site to %s' ), connectedSite.name ),
message: __( 'Studio was unable to export the site.' ),
title: sprintf( __( 'Error pushing to %s' ), connectedSite.name ),
message: __(
'An error occurred while pushing the site. If this problem persists, please contact support.'
),
Comment on lines +143 to +145
Copy link
Member

Choose a reason for hiding this comment

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

👍

Copy link
Contributor

Choose a reason for hiding this comment

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

That's a good call.

When I was introducing the error message, I was looking at it from the perspective of a site export step (as a part of the whole push process). However, from the user's perspective I think it makes to use the error message you have proposed here.

error,
} );
return;
}
Expand Down
1 change: 0 additions & 1 deletion src/hooks/use-chat-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ const parseWpCliOutput = ( stdout: string, defaultValue: string[] ): string[] =>
const data = JSON.parse( stdout );
return data?.map( ( item: { name: string } ) => item.name ) || [];
} catch ( error ) {
console.error( error, stdout );
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was logging to the browser console, but the user doesn't have access to that.

Sentry.captureException( error, {
extra: { stdout },
} );
Expand Down
14 changes: 12 additions & 2 deletions src/lib/import-export/export/export-database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,25 @@ export async function exportDatabaseToMultipleFiles(
}

const tablesResult = await server.executeWpCliCommand(
`sqlite tables --format=csv --require=/tmp/sqlite-command/command.php`
`sqlite tables --format=json --require=/tmp/sqlite-command/command.php`
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Switching to JSON, we can easily validate the output. If HTML or text content leaks into the command output, it will always produce invalid JSON.

);
if ( tablesResult.stderr ) {
throw new Error( `Database export failed: ${ tablesResult.stderr }` );
}
if ( tablesResult.exitCode ) {
throw new Error( 'Database export failed' );
}
const tables = tablesResult.stdout.split( ',' );

let tables;

try {
tables = JSON.parse( tablesResult.stdout );
Copy link
Member

Choose a reason for hiding this comment

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

After applying Automattic/wp-cli-sqlite-command#9 it worked as expected, producing the output:

[
  'wp_users',
  'wp_usermeta',
  'wp_termmeta',
  'wp_terms',
  'wp_term_taxonomy',
  'wp_term_relationships',
  'wp_commentmeta',
  'wp_comments',
  'wp_links',
  'wp_options',
  'wp_postmeta',
  'wp_posts'
]

Very smart! 💡

} catch ( error ) {
console.error(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This goes to the app log.

`Could not get list of database tables. The WP CLI output: ${ tablesResult.stdout }`
);
throw new Error( 'Could not get list of database tables to export.' );
Copy link
Contributor Author

Choose a reason for hiding this comment

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

And this is the message that would end up in the dialog displayed to the user.

}

const tmpFiles: string[] = [];

Expand Down
26 changes: 22 additions & 4 deletions src/lib/import-export/export/exporters/default-exporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,10 +273,19 @@ export class DefaultExporter extends EventEmitter implements Exporter {

if ( stderr ) {
console.error( `Could not get information about plugins: ${ stderr }` );
return [];
throw new Error(
'Could not get information about installed plugins to create meta.json file.'
);
}

return JSON.parse( stdout );
try {
return JSON.parse( stdout );
} catch ( error ) {
console.error( `Could not parse plugins list. The WP CLI output: ${ stdout }` );
throw new Error(
'Could not parse information about installed plugins to create meta.json file.'
);
}
}

private async getSiteThemes( site_id: string ) {
Expand All @@ -292,9 +301,18 @@ export class DefaultExporter extends EventEmitter implements Exporter {

if ( stderr ) {
console.error( `Could not get information about themes: ${ stderr }` );
return [];
throw new Error(
'Could not get information about installed themes to create meta.json file.'
);
}

return JSON.parse( stdout );
try {
return JSON.parse( stdout );
} catch ( error ) {
console.error( `Could not parse themes list. The WP CLI output: ${ stdout }` );
throw new Error(
'Could not parse information about installed themes to create meta.json file.'
);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ describe( 'DefaultExporter', () => {
case /theme list/.test( command ):
return { stdout: '[{"name":"twentytwentyfour","status":"active","version":"1.0"}]' };
case /tables/.test( command ):
return { stdout: defaultTableNames.join( ',' ) };
return { stdout: JSON.stringify( defaultTableNames ) };
default:
return { stderr: null };
}
Expand Down Expand Up @@ -323,7 +323,7 @@ describe( 'DefaultExporter', () => {
expect( canHandle ).toBe( false );
} );

it( 'should not fail when can not get plugin or theme details', async () => {
it( 'should fail when can not get plugin or theme details', async () => {
Copy link
Member

Choose a reason for hiding this comment

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

👍

( SiteServer.get as jest.Mock ).mockReturnValue( {
details: { path: '/path/to/site' },
executeWpCliCommand: jest.fn( function ( command: string ) {
Expand All @@ -338,10 +338,9 @@ describe( 'DefaultExporter', () => {
} );

const exporter = new DefaultExporter( mockOptions );
await exporter.export();

expect( mockArchiver.file ).toHaveBeenCalledWith( '/tmp/studio_export_123/meta.json', {
name: 'meta.json',
} );
await expect( exporter.export() ).rejects.toThrow(
'Could not get information about installed plugins to create meta.json file.'
);
} );
} );
Loading