Implement import/export in CLI - #2963
Conversation
- Move process.emit restore into finally block in push command - Emit SYNC_EVENTS.FAILED in catch blocks for both pull and push - Extract poll constants and parseSyncOptions to shared locations
…gs, double-cancel, reorder validation, remove re-exports
f26f025 to
9ab29a1
Compare
| // We configure `trash` as an external module, since it includes a native macOS binary that Vite | ||
| // inlines as a base64 string, which produces a runtime error. Since `trash` is also an ESM-only | ||
| // module, we need to import it dynamically (since Rollup doesn't get a chance to process it) | ||
| const trash = ( await import( 'trash' ) ).default; |
There was a problem hiding this comment.
Not related to the core changes in this PR, but we output ESM now, and trash is always configured as an external module when Vite bundles, so this is no longer an issue.
| break; | ||
|
|
||
| case ExportEvents.BACKUP_CREATE_PROGRESS: { | ||
| const progressData = data as BackupCreateProgressEventData; |
There was a problem hiding this comment.
I'd love to avoid type assertions for the event payloads, but since this copies the logic we already have in the app, I decided to stick with that for now.
| logger.reportStart( | ||
| LoggerAction.INSTALL_SQLITE, | ||
| __( 'Setting up SQLite integration, if needed…' ) | ||
| ); | ||
| await keepSqliteIntegrationUpdated( siteFolder ); | ||
| logger.reportSuccess( __( 'SQLite integration configured as needed' ) ); |
There was a problem hiding this comment.
It's important to keep the SQLite integration up to date whenever we work with the site database. Exporting the site is one such example.
| const zipUrl = getWordPressVersionUrl( wp ); | ||
|
|
||
| const [ response, exitPhp ] = await runWpCliCommand( sitePath, phpVersion, [ | ||
| await using command = await runWpCliCommand( sitePath, phpVersion, [ |
There was a problem hiding this comment.
If you're not familiar with the using keyword, I recommend a quick read-up on JavaScript resource management. Playground already uses this internally.
In short, it saves us from having to call a cleanup function (exitPhp) because the JS runtime automatically calls the method identified by Symbol.dispose when exiting the scope. Very nifty.
There was a problem hiding this comment.
TIL
I wonder if we could refactor our config files lock/unlock pattern with this approach.
Obviously not a blocker for this PR, and it could be a follow-up
There was a problem hiding this comment.
That's an interesting candidate 👍 There's some nuance to how we handle that, though – it sometimes takes a while for the code to exit the current scope after writing the config file. Worth exploring, though.
connectToDaemon and disconnectFromDaemon also seem like good candidates to me.
There was a problem hiding this comment.
See my comment in apps/cli/commands/site/set.ts about JavaScript resource management to understand what's going on here.
| "@php-wasm/node": "3.1.15", | ||
| "@php-wasm/universal": "3.1.15", | ||
| "@php-wasm/util": "3.1.15", | ||
| "@php-wasm/node": "3.1.13", | ||
| "@php-wasm/universal": "3.1.13", | ||
| "@php-wasm/util": "3.1.13", | ||
| "@vscode/sudo-prompt": "^9.3.2", | ||
| "@wordpress/i18n": "^6.14.0", | ||
| "@wp-playground/blueprints": "3.1.15", | ||
| "@wp-playground/cli": "3.1.15", | ||
| "@wp-playground/common": "3.1.15", | ||
| "@wp-playground/storage": "3.1.15", | ||
| "@wp-playground/wordpress": "3.1.15", | ||
| "@wp-playground/blueprints": "3.1.13", | ||
| "@wp-playground/cli": "3.1.13", | ||
| "@wp-playground/common": "3.1.13", | ||
| "@wp-playground/storage": "3.1.13", | ||
| "@wp-playground/wordpress": "3.1.13", |
There was a problem hiding this comment.
I had to downgrade these packages because I couldn't get WP-CLI to play ball. See p1775051878031829-slack-C04GESRBWKW
There was a problem hiding this comment.
I was able to revert this change after copying the fix from #2980
bcotrim
left a comment
There was a problem hiding this comment.
LGTM 👍
Added a few comments but no blockers.
I was considering if we should share the importer and exporters instead of copying, but since we are eventually removing the operations from Studio app and using the CLI, I think it makes sense to avoid any side effects now and just live with copied files.
| describe: __( 'Export site to a backup file' ), | ||
| builder: ( yargs ) => { | ||
| return yargs | ||
| .positional( 'export-file', { |
There was a problem hiding this comment.
Should we allow this to be empty and use a generated file name in the current folder?
I think it would be a good UX improvement
There was a problem hiding this comment.
That's fair. I've made this change 👍
|
|
||
| wasServerRunning = !! ( await isServerRunning( site.id ) ); | ||
|
|
||
| if ( wasServerRunning ) { |
There was a problem hiding this comment.
Nit: Could we move this after the import file and site path directory validations? It could potentially prevent an unnecessary site restart.
|
|
||
| // Something in Playground makes it so the front-end of the site sometimes returns an error page | ||
| // on the first request. Send that first request from here to hide the error from the user. | ||
| const siteUrl = getSiteUrl( site ); |
There was a problem hiding this comment.
Was this reported to the Playground team?
The workaround isn't particularly problematic but I wonder what is the root cause for it, and if it has other side effects.
There was a problem hiding this comment.
Not yet, no. I'll put it on my to-do list. I encountered it several times, but I haven't isolated the error yet, so I'd like to do that before reporting.
| const zipUrl = getWordPressVersionUrl( wp ); | ||
|
|
||
| const [ response, exitPhp ] = await runWpCliCommand( sitePath, phpVersion, [ | ||
| await using command = await runWpCliCommand( sitePath, phpVersion, [ |
There was a problem hiding this comment.
TIL
I wonder if we could refactor our config files lock/unlock pattern with this approach.
Obviously not a blocker for this PR, and it could be a follow-up
| sprintf( __( 'Pulled from %s (%s)' ), remoteSite.name, remoteSite.url ) | ||
| ); | ||
| } finally { | ||
| fs.rmSync( tempDir, { recursive: true, force: true } ); |
There was a problem hiding this comment.
This was a pre-existing issue from my initial PR.
Concurrent sync operations, even for different sites could break since the tempDir is always the same.
Studio app uses mkdtemp
There was a problem hiding this comment.
Great catch 👍
There was a problem hiding this comment.
I made the corresponding update in the push command, too.
- Default values for export files. - Replace `--only` with `--mode` for export command. Users can choose between "full" and "db". The `--mode` option is required and defaults to `full`.
Yeah, I think so, too. We call WP-CLI in several places in the importer and exporter, and it would be an unnecessary headache to rewrite a shared version of the code to inject a WP-CLI caller that wraps the CLI/app logic. However, this assumes we proceed soon to rewrite the app's import/export logic to depend on the CLI. I've addressed your other review comments, @bcotrim. I also changed the export command to use a |
bcotrim
left a comment
There was a problem hiding this comment.
Thanks for making the changes
LGTM 👍
d5047bc
into
add_cli_sync_and_import_operations
* CLI: Add sync push/pull commands (#2878) * Add sync push/pull CLI commands with shared Zod schemas and event pipeline * Fix CLI build by replacing stream/promises with stream import * Fix lint errors in sync store files * Add progress tracking, --archive flag, and better error handling to sync commands * Add interactive selective sync to CLI push and pull commands * Extract shared sync logic to common and fix selective pull bugs * Fix pre-existing TS error in process.emit override * Fix lint error in process.emit override * Remove unused sync constants re-export * Remove unnecessary comment in sync-operations-slice * Add esc cancel support and unify help tip layout across prompts * Fix return types for sync selector cancel support * Use Zod schema for SyncOption validation instead of manual VALID_OPTIONS * Fix sync event lifecycle bugs and extract shared constants - Move process.emit restore into finally block in push command - Emit SYNC_EVENTS.FAILED in catch blocks for both pull and push - Extract poll constants and parseSyncOptions to shared locations * Add sync size validation to CLI push and pull commands * Simplify type annotations in sync push and pull commands * Remove unused sync CLI events * Add --site arg, stale-progress polling, and fix push archive flow * Rename --site arg to --remote-site in sync commands * Address PR feedback: move try/catch to handlers, parse options in yargs, double-cancel, reorder validation, remove re-exports * Move logger to module scope in sync pull/push commands * Remove type re-exports, import directly from common * Fix import order in sync test * Remove dead tusUpload and use raw progress for stall detection * Address PR feedback: use normalizeHostname, chalk, camelCase schema, remove type assertion * Address PR feedback: camelCase schemas, static tus import, sort by name, remove re-exports --------- Co-authored-by: Fredrik Rombach Ekelund <fredrik@f26d.dev> * Implement import/export in CLI (#2963) * Add sync push/pull CLI commands with shared Zod schemas and event pipeline * Fix CLI build by replacing stream/promises with stream import * Fix lint errors in sync store files * Add progress tracking, --archive flag, and better error handling to sync commands * Add interactive selective sync to CLI push and pull commands * Extract shared sync logic to common and fix selective pull bugs * Fix pre-existing TS error in process.emit override * Fix lint error in process.emit override * Remove unused sync constants re-export * Remove unnecessary comment in sync-operations-slice * Add esc cancel support and unify help tip layout across prompts * Fix return types for sync selector cancel support * Use Zod schema for SyncOption validation instead of manual VALID_OPTIONS * Fix sync event lifecycle bugs and extract shared constants - Move process.emit restore into finally block in push command - Emit SYNC_EVENTS.FAILED in catch blocks for both pull and push - Extract poll constants and parseSyncOptions to shared locations * Add sync size validation to CLI push and pull commands * Simplify type annotations in sync push and pull commands * Remove unused sync CLI events * Add --site arg, stale-progress polling, and fix push archive flow * Rename --site arg to --remote-site in sync commands * Address PR feedback: move try/catch to handlers, parse options in yargs, double-cancel, reorder validation, remove re-exports * Move logger to module scope in sync pull/push commands * Remove type re-exports, import directly from common * Fix import order in sync test * First iteration of the `import` CLI command * Initial version of export command * Polishing * Fix and refine * Import command: restart site on error, too * Allow .sql exports without `--only` flag * Improved error handling * More error handling * Move files * Wire up import/export logic to sync commands * Install missing dependency * Cleanup and test fixes * Windows-friendly paths in test * Fix issues identified in review * Address review comments * Stray console.log * Revert Playground downgrades and implement WP-CLI fix * Disallow database-only exports for .zip and .tar.gz files * Numerous changes - Default values for export files. - Replace `--only` with `--mode` for export command. Users can choose between "full" and "db". The `--mode` option is required and defaults to `full`. * Address review comments --------- Co-authored-by: bcotrim <bernardo.cotrim@a8c.com> Co-authored-by: Bernardo Cotrim <bmmcotrim@gmail.com> * glob dev dependency for the CLI * Fix Vite config * Move new commands to topmost scope Also, sort all CLI commands alphabetically * Use search component in sync site picker and add i18n to CLI strings (#3014) --------- Co-authored-by: Bernardo Cotrim <bmmcotrim@gmail.com> Co-authored-by: bcotrim <bernardo.cotrim@a8c.com>
Related issues
How AI was used in this PR
Primarily to review my implementation and help me preemptively fix several issues.
Proposed Changes
Tip
I'm happy to pair review this PR.
This one's a biggie. It adds
site importandsite exportCLI commands. I've copied the core import/export logic fromapps/studioand adapted it to CLI conditions (the types are slightly different, we call WP-CLI in different ways, etc).Moreover, this PR finalizes the
sync pullandsync pushcommands by wiring up the import/export logic there, too.Testing Instructions
npm run cli:buildnode apps/cli/dist/cli/main.mjs site exportcommand. Explore the flags and try different combinations.node apps/cli/dist/cli/main.mjs site importcommand. Explore the flags and try different combinations.node apps/cli/dist/cli/main.mjs sync pullcommand.node apps/cli/dist/cli/main.mjs sync pushcommand.Pre-merge Checklist