Skip to content

Refactor preview site updating to depend on CLI #1223

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

Merged
merged 35 commits into from
Apr 22, 2025
Merged
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
e12452a
[WIP] Refactor Studio to depend on CLI for preview site creation
fredrikekelund Apr 14, 2025
86440a0
More changes
fredrikekelund Apr 15, 2025
28ea3dd
Fix
fredrikekelund Apr 15, 2025
67ff103
Use node's child_process module to fork CLI process
fredrikekelund Apr 15, 2025
d78606d
Tests
fredrikekelund Apr 15, 2025
f57a538
Merge branch 'trunk' into f26d/depend-on-cli-preview-site-creation
fredrikekelund Apr 15, 2025
476a587
useGetSnapshotUsage
fredrikekelund Apr 15, 2025
fbb1aa9
Build CLI before starting dev version of app
fredrikekelund Apr 15, 2025
2d0cf49
Merge branch 'trunk' into f26d/depend-on-cli-preview-site-creation
fredrikekelund Apr 16, 2025
0a091f2
Tweaks
fredrikekelund Apr 16, 2025
f8134c5
Fixes
fredrikekelund Apr 16, 2025
d91f3ac
preview ➡️ snapshot
fredrikekelund Apr 16, 2025
f9c162d
Improved progress bar
fredrikekelund Apr 16, 2025
0213f83
Display notice when operation completes
fredrikekelund Apr 16, 2025
b7e5811
Update zod
fredrikekelund Apr 16, 2025
e34f4a7
Remove useArchiveSite hook
fredrikekelund Apr 16, 2025
0ba4f63
Fix test
fredrikekelund Apr 16, 2025
709a76d
Merge branch 'trunk' into f26d/depend-on-cli-preview-site-creation
fredrikekelund Apr 16, 2025
8ac1eb2
Modify exitCode in Logger::reportError
fredrikekelund Apr 17, 2025
93f7650
Merge branch 'trunk' into f26d/depend-on-cli-preview-site-creation
fredrikekelund Apr 17, 2025
4d6657e
Set exit code on error and fix outputFormat parsing
fredrikekelund Apr 17, 2025
8b8bc5e
Shorten
fredrikekelund Apr 17, 2025
6404aa3
Fix outputFormat parsing
fredrikekelund Apr 17, 2025
7fef103
Clarify naming
fredrikekelund Apr 17, 2025
1bea58b
Refactor preview site updating to depend on CLI
fredrikekelund Apr 17, 2025
5e45c01
Clean up
fredrikekelund Apr 17, 2025
418a701
Tweak
fredrikekelund Apr 17, 2025
8559207
Merge branch 'trunk' into f26d/depend-on-cli-preview-site-creation
fredrikekelund Apr 17, 2025
ff16bb2
Merge branch 'f26d/depend-on-cli-preview-site-creation' into f26d/dep…
fredrikekelund Apr 17, 2025
5f71f58
Merge branch 'trunk' into f26d/depend-on-cli-preview-site-creation
fredrikekelund Apr 17, 2025
f49afbb
Merge branch 'f26d/depend-on-cli-preview-site-creation' into f26d/dep…
fredrikekelund Apr 17, 2025
a66f10e
Merge branch 'trunk' into f26d/depend-on-cli-preview-site-creation
fredrikekelund Apr 22, 2025
4b961db
Tweaks
fredrikekelund Apr 22, 2025
66f76ee
Merge branch 'f26d/depend-on-cli-preview-site-creation' into f26d/dep…
fredrikekelund Apr 22, 2025
15401f3
Merge branch 'trunk' into f26d/depend-on-cli-preview-site-updating
fredrikekelund Apr 22, 2025
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
26 changes: 13 additions & 13 deletions cli/commands/preview/create.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import os from 'os';
import path from 'path';
import { __, sprintf } from '@wordpress/i18n';
import { CreateLoggerAction as LoggerAction } from 'cli/commands/preview/logger-actions';
import { uploadArchive, waitForSiteReady } from 'cli/lib/api';
import { getAuthToken } from 'cli/lib/appdata';
import { createArchive, cleanup } from 'cli/lib/archive';
Expand All @@ -9,14 +10,6 @@ import { validateSiteFolder, validateSiteSize } from 'cli/lib/validation';
import { Logger, LoggerError } from 'cli/logger';
import { RegisterCommand, OutputFormat } from 'cli/types';

enum LoggerAction {
VALIDATE = 'validate',
ARCHIVE = 'archive',
UPLOAD = 'upload',
READY = 'ready',
APPDATA = 'appdata',
}

async function runCommand( siteFolder: string, outputFormat?: OutputFormat ): Promise< void > {
const archivePath = path.join(
os.tmpdir(),
Expand Down Expand Up @@ -46,8 +39,15 @@ async function runCommand( siteFolder: string, outputFormat?: OutputFormat ): Pr
);

logger.reportStart( LoggerAction.APPDATA, __( 'Saving preview site to Studio...' ) );
await upsertPreviewSiteInAppdata( siteFolder, uploadResponse.site_id, uploadResponse.site_url );
const snapshot = await upsertPreviewSiteInAppdata(
siteFolder,
uploadResponse.site_id,
uploadResponse.site_url
);
logger.reportSuccess( __( 'Preview site saved to Studio' ) );

logger.reportKeyValuePair( 'name', snapshot.name );
logger.reportKeyValuePair( 'url', snapshot.url );
} catch ( error ) {
if ( error instanceof LoggerError ) {
logger.reportError( error );
Expand All @@ -60,14 +60,14 @@ async function runCommand( siteFolder: string, outputFormat?: OutputFormat ): Pr
}
}

export const registerCommand: RegisterCommand = ( program ) => {
program
export const registerCommand: RegisterCommand = ( parentCommand, rootCommand = parentCommand ) => {
parentCommand
.command( 'go [folder]' )
.description(
__( 'Create a preview site from the specified folder (defaults to current directory)' )
)
.action( async ( siteFolder: string = process.cwd() ) => {
const options = program.opts();
await runCommand( siteFolder, options.outputFormat );
const outputFormat = rootCommand.opts().outputFormat;
await runCommand( siteFolder, outputFormat );
} );
};
14 changes: 5 additions & 9 deletions cli/commands/preview/delete.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import { __ } from '@wordpress/i18n';
import { DeleteLoggerAction as LoggerAction } from 'cli/commands/preview/logger-actions';
import { deleteSnapshot } from 'cli/lib/api';
import { getAuthToken } from 'cli/lib/appdata';
import { deleteSnapshotFromAppdata, getSnapshotsFromAppdata } from 'cli/lib/snapshots';
import { normalizeHostname } from 'cli/lib/utils';
import { Logger, LoggerError } from 'cli/logger';
import { RegisterCommand, OutputFormat } from 'cli/types';

export enum LoggerAction {
VALIDATE = 'validate',
DELETE = 'delete',
}

async function runCommand( host: string, outputFormat?: OutputFormat ): Promise< void > {
const logger = new Logger< LoggerAction >( outputFormat );

Expand Down Expand Up @@ -43,13 +39,13 @@ async function runCommand( host: string, outputFormat?: OutputFormat ): Promise<
}
}

export const registerCommand: RegisterCommand = ( program ) => {
program
export const registerCommand: RegisterCommand = ( parentCommand, rootCommand = parentCommand ) => {
parentCommand
.command( 'delete <host>' )
.description( __( 'Delete a preview site' ) )
.action( async ( host: string ) => {
const options = program.opts();
const outputFormat = rootCommand.opts().outputFormat;
const normalizedHost = normalizeHostname( host );
await runCommand( normalizedHost, options.outputFormat );
await runCommand( normalizedHost, outputFormat );
} );
};
14 changes: 5 additions & 9 deletions cli/commands/preview/list.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import { __, _n, sprintf } from '@wordpress/i18n';
import { ListLoggerAction as LoggerAction } from 'cli/commands/preview/logger-actions';
import { getAuthToken } from 'cli/lib/appdata';
import { getSnapshotCliTable } from 'cli/lib/output';
import { getSnapshotsFromAppdata } from 'cli/lib/snapshots';
import { validateSiteFolder } from 'cli/lib/validation';
import { Logger, LoggerError } from 'cli/logger';
import { RegisterCommand, OutputFormat } from 'cli/types';

export enum LoggerAction {
VALIDATE = 'validate',
LOAD = 'load',
}

async function runCommand( siteFolder: string, outputFormat?: OutputFormat ): Promise< void > {
const logger = new Logger< LoggerAction >( outputFormat );

Expand Down Expand Up @@ -44,14 +40,14 @@ async function runCommand( siteFolder: string, outputFormat?: OutputFormat ): Pr
}
}

export const registerCommand: RegisterCommand = ( program ) => {
program
export const registerCommand: RegisterCommand = ( parentCommand, rootCommand = parentCommand ) => {
parentCommand
.command( 'list [folder]' )
.description(
__( 'List preview sites for the specified folder (defaults to current directory)' )
)
.action( async ( siteFolder: string = process.cwd() ) => {
const options = program.opts();
await runCommand( siteFolder, options.outputFormat );
const outputFormat = rootCommand.opts().outputFormat;
await runCommand( siteFolder, outputFormat );
} );
};
28 changes: 28 additions & 0 deletions cli/commands/preview/logger-actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Store the actions in a separate file to avoid Webpack issues when importing them in the Studio
// source code

export enum CreateLoggerAction {
VALIDATE = 'validate',
ARCHIVE = 'archive',
UPLOAD = 'upload',
READY = 'ready',
APPDATA = 'appdata',
}

export enum DeleteLoggerAction {
VALIDATE = 'validate',
DELETE = 'delete',
}

export enum ListLoggerAction {
VALIDATE = 'validate',
LOAD = 'load',
}

export enum UpdateLoggerAction {
VALIDATE = 'validate',
ARCHIVE = 'archive',
UPLOAD = 'upload',
READY = 'ready',
APPDATA = 'appdata',
}
26 changes: 13 additions & 13 deletions cli/commands/preview/update.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import os from 'node:os';
import path from 'node:path';
import { __, _n, sprintf } from '@wordpress/i18n';
import { UpdateLoggerAction as LoggerAction } from 'cli/commands/preview/logger-actions';
import { uploadArchive, waitForSiteReady } from 'cli/lib/api';
import { getAuthToken } from 'cli/lib/appdata';
import { cleanup, createArchive } from 'cli/lib/archive';
Expand All @@ -10,14 +11,6 @@ import { validateSiteFolder } from 'cli/lib/validation';
import { Logger, LoggerError } from 'cli/logger';
import { RegisterCommand, OutputFormat } from 'cli/types';

enum LoggerAction {
VALIDATE = 'validate',
ARCHIVE = 'archive',
UPLOAD = 'upload',
READY = 'ready',
APPDATA = 'appdata',
}

async function runCommand(
siteFolder: string,
host: string,
Expand Down Expand Up @@ -59,8 +52,15 @@ async function runCommand(
);

logger.reportStart( LoggerAction.APPDATA, __( 'Saving preview site to Studio...' ) );
await upsertPreviewSiteInAppdata( siteFolder, uploadResponse.site_id, uploadResponse.site_url );
const snapshot = await upsertPreviewSiteInAppdata(
siteFolder,
uploadResponse.site_id,
uploadResponse.site_url
);
logger.reportSuccess( __( 'Preview site saved to Studio' ) );

logger.reportKeyValuePair( 'name', snapshot.name );
logger.reportKeyValuePair( 'url', snapshot.url );
} catch ( error ) {
if ( error instanceof LoggerError ) {
logger.reportError( error );
Expand All @@ -73,16 +73,16 @@ async function runCommand(
}
}

export const registerCommand: RegisterCommand = ( program ) => {
program
export const registerCommand: RegisterCommand = ( parentCommand, rootCommand = parentCommand ) => {
parentCommand
.command( 'update [folder]' )
.description(
__( 'Update preview site for the specified folder (defaults to current directory)' )
)
.requiredOption( '-h, --host <host>', __( 'Host of the preview site to update' ) )
.action( async ( siteFolder: string = process.cwd(), options ) => {
const parentOptions = program.opts();
const outputFormat = rootCommand.opts().outputFormat;
const normalizedHost = normalizeHostname( options.host );
await runCommand( siteFolder, normalizedHost, parentOptions.outputFormat );
await runCommand( siteFolder, normalizedHost, outputFormat );
} );
};
18 changes: 10 additions & 8 deletions cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ import { version } from 'cli/package.json';
async function main() {
await loadTranslations();

const program = new Command();
const studioCommand = new Command();

program
studioCommand
.name( 'studio' )
.description( __( 'Studio by WordPress.com CLI' ) )
.version( version )
Expand All @@ -32,14 +32,16 @@ async function main() {
.hideHelp()
);

const previewCommand = program.command( 'preview' ).description( __( 'Manage preview sites' ) );
const previewCommand = studioCommand
.command( 'preview' )
.description( __( 'Manage preview sites' ) );

registerPreviewCreateCommand( program );
registerPreviewListCommand( previewCommand );
registerPreviewDeleteCommand( previewCommand );
registerPreviewUpdateCommand( previewCommand );
registerPreviewCreateCommand( studioCommand );
registerPreviewListCommand( previewCommand, studioCommand );
registerPreviewDeleteCommand( previewCommand, studioCommand );
registerPreviewUpdateCommand( previewCommand, studioCommand );

program.parse( process.argv );
studioCommand.parse( process.argv );
}

main();
14 changes: 12 additions & 2 deletions cli/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export class LoggerError extends Error {
export class Logger< T extends string > {
protected readonly outputFormat: OutputFormat;
private spinner: Ora;
private currentAction: T | null;
private currentAction: T | 'keyValuePair' | null;

constructor( outputFormat: OutputFormat ) {
this.outputFormat = outputFormat;
Expand Down Expand Up @@ -66,7 +66,11 @@ export class Logger< T extends string > {
this.currentAction = null;
}

public reportError( error: LoggerError ) {
public reportError( error: LoggerError, isFatal = true ) {
if ( isFatal ) {
process.exitCode = 1;
}

if ( this.outputFormat === 'json' ) {
console.error(
JSON.stringify( { action: this.currentAction, status: 'fail', message: error.message } )
Expand All @@ -77,4 +81,10 @@ export class Logger< T extends string > {

this.currentAction = null;
}

public reportKeyValuePair( key: string, value: string ) {
if ( this.outputFormat === 'json' ) {
console.log( JSON.stringify( { action: 'keyValuePair', key, value } ) );
}
}
}
2 changes: 1 addition & 1 deletion cli/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from 'commander';

export type OutputFormat = undefined | 'json';
export type RegisterCommand = ( program: Command ) => void;
export type RegisterCommand = ( parentCommand: Command, rootCommand?: Command ) => void;
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@
"directory": "/"
},
"scripts": {
"prestart": "npm run cli:build",
"start": "electron-forge start",
"start-wayland": "electron-forge start -- --enable-features=UseOzonePlatform --ozone-platform=wayland .",
"start-wayland": "npm run prestart && electron-forge start -- --enable-features=UseOzonePlatform --ozone-platform=wayland .",
"postinstall": "patch-package && ts-node scripts/download-wp-server-files.ts && node ./scripts/download-available-site-translations.mjs",
"package": "electron-forge package",
"make": "electron-forge make",
Expand Down Expand Up @@ -157,7 +158,7 @@
"wpcom": "^5.4.2",
"yargs": "17.7.2",
"yauzl": "^3.2.0",
"zod": "^3.24.1"
"zod": "^3.24.2"
},
"optionalDependencies": {
"appdmg": "^0.6.6"
Expand Down
7 changes: 7 additions & 0 deletions src/components/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,24 @@ import { cx } from 'src/lib/cx';
import { getIpcApi } from 'src/lib/get-ipc-api';
import { UserSettings } from 'src/modules/user-settings';
import { WhatsNewModal, useWhatsNew } from 'src/modules/whats-new';
import { useAppDispatch } from 'src/stores';
import { snapshotThunks } from 'src/stores/snapshot-slice';

export default function App() {
useLocalizationSupport();
const { needsOnboarding } = useOnboarding();
const { isSidebarVisible, toggleSidebar } = useSidebarVisibility();
const { showWhatsNew, closeWhatsNew } = useWhatsNew();
const dispatch = useAppDispatch();

useEffect( () => {
getIpcApi().setupAppMenu( { needsOnboarding } );
}, [ needsOnboarding ] );

useEffect( () => {
dispatch( snapshotThunks.getSnapshots() );
}, [ dispatch ] );

return (
<>
{ needsOnboarding ? (
Expand Down
Loading
Loading