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
43 changes: 9 additions & 34 deletions apps/studio/src/ipc-handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ import { validateBlueprintData } from '@studio/common/lib/blueprint-validation';
import { parseCliError, errorMessageContains } from '@studio/common/lib/cli-error';
import { getConnectedWpcomSitesForLocalSite } from '@studio/common/lib/connected-sites';
import { createDeployIgnoreFilter } from '@studio/common/lib/deploy-ignore';
import { extractZip } from '@studio/common/lib/extract-zip';
import {
calculateDirectorySizeForArchive,
isWordPressDirectory,
Expand Down Expand Up @@ -84,6 +83,11 @@ import { SYNC_IGNORE_DEFAULTS } from '@studio/common/lib/sync/constants';
import { shouldExcludeFromSync } from '@studio/common/lib/sync/exclude-from-sync';
import { shouldLimitDepth } from '@studio/common/lib/sync/tree-utils';
import { isWordPressDevVersion } from '@studio/common/lib/wordpress-version-utils';
import {
cleanupBlueprintTempDir as cleanupBlueprintTempDirShared,
extractBlueprintBundle as extractBlueprintBundleShared,
type ExtractedBlueprintBundle,
} from '@studio/common/sites/blueprint-extract';
import { __, sprintf, LocaleData, defaultI18n } from '@wordpress/i18n';
import {
MACOS_TRAFFIC_LIGHT_POSITION,
Expand Down Expand Up @@ -203,6 +207,7 @@ export {
pauseSyncUpload,
pullSiteFromLive,
pushArchive,
pushSiteToLive,
removeSyncBackup,
resumeSyncUpload,
updateConnectedWpcomSites,
Expand Down Expand Up @@ -2281,45 +2286,15 @@ export async function readBlueprintFile(
export async function extractBlueprintBundle(
_event: IpcMainInvokeEvent,
zipFilePath: string
): Promise< {
blueprintJson: Blueprint[ 'blueprint' ];
blueprintJsonPath: string;
tempDir: string;
} > {
const resolvedZipPath = nodePath.resolve( zipFilePath );
const tempDir = await fsPromises.mkdtemp(
nodePath.join( os.tmpdir(), 'studio-blueprint-bundle-' )
);

try {
await extractZip( resolvedZipPath, tempDir );

const blueprintJsonPath = nodePath.join( tempDir, 'blueprint.json' );
try {
await fsPromises.access( blueprintJsonPath );
} catch {
throw new Error(
__(
'No blueprint.json found in the ZIP file. Please ensure the ZIP contains a blueprint.json at its root.'
)
);
}

const fileContents = await fsPromises.readFile( blueprintJsonPath, 'utf-8' );
const blueprintJson = JSON.parse( fileContents );

return { blueprintJson, blueprintJsonPath, tempDir };
} catch ( error ) {
await fsPromises.rm( tempDir, { recursive: true, force: true } );
throw error;
}
): Promise< ExtractedBlueprintBundle > {
return extractBlueprintBundleShared( zipFilePath );
}

export async function cleanupBlueprintTempDir(
_event: IpcMainInvokeEvent,
tempDir: string
): Promise< void > {
await removeBlueprintTempDir( tempDir );
await cleanupBlueprintTempDirShared( tempDir );
}

export async function setWindowControlVisibility( event: IpcMainInvokeEvent, visible: boolean ) {
Expand Down
109 changes: 6 additions & 103 deletions apps/studio/src/modules/cli/lib/cli-site-creator.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { type SiteFileAccess } from '@studio/common/lib/site-file-access';
import { siteModeFromRuntime, type SiteRuntime } from '@studio/common/lib/site-runtime';
import { isWordPressDevVersion } from '@studio/common/lib/wordpress-version-utils';
import { SiteCommandLoggerAction } from '@studio/common/logger-actions';
import { buildSiteCreateArgs, type SiteCreateOptions } from '@studio/common/sites/create';
import { z } from 'zod';
import { sendIpcEventToRenderer } from 'src/ipc-utils';
import { executeCliCommand } from './execute-command';
import type { Blueprint } from '@wp-playground/blueprints';

const cliEventSchema = z.discriminatedUnion( 'action', [
z.object( {
Expand All @@ -29,38 +23,12 @@ interface CreateSiteResult {
running: boolean;
}

export interface CreateSiteOptions {
path: string;
name?: string;
wpVersion?: string;
phpVersion?: string;
runtime?: SiteRuntime;
fileAccess?: SiteFileAccess;
customDomain?: string;
enableHttps?: boolean;
siteId?: string;
blueprint?: Blueprint;
originalBlueprintPath?: string;
adminUsername?: string;
adminPassword?: string;
adminEmail?: string;
noStart?: boolean;
}
export type CreateSiteOptions = SiteCreateOptions;

export async function createSiteViaCli( options: CreateSiteOptions ): Promise< CreateSiteResult > {
const args = buildCliArgs( options );
const { args, cleanup } = buildSiteCreateArgs( options );
const siteId = options.siteId;

let blueprintTempPath: string | undefined;
if ( options.blueprint ) {
blueprintTempPath = path.join( os.tmpdir(), `studio-blueprint-${ Date.now() }.json` );
fs.writeFileSync( blueprintTempPath, JSON.stringify( options.blueprint ) );
args.push( '--blueprint', blueprintTempPath );
if ( options.originalBlueprintPath ) {
args.push( '--original-blueprint-path', options.originalBlueprintPath );
}
}

return new Promise( ( resolve, reject ) => {
const result: Partial< CreateSiteResult > = {};
const [ emitter ] = executeCliCommand( args, { output: 'capture', logPrefix: siteId } );
Expand Down Expand Up @@ -97,7 +65,7 @@ export async function createSiteViaCli( options: CreateSiteOptions ): Promise< C
} );

emitter.on( 'success', () => {
cleanupTempFile( blueprintTempPath );
cleanup();
if ( ! result.id ) {
reject( new Error( 'CLI create site succeeded but no site ID received' ) );
return;
Expand All @@ -110,79 +78,14 @@ export async function createSiteViaCli( options: CreateSiteOptions ): Promise< C
} );

emitter.on( 'failure', ( { error } ) => {
cleanupTempFile( blueprintTempPath );
cleanup();
error.baseMessage = 'Failed to create site';
reject( error );
} );

emitter.on( 'error', ( { error } ) => {
cleanupTempFile( blueprintTempPath );
cleanup();
reject( error );
} );
} );
}

function buildCliArgs( options: CreateSiteOptions ): string[] {
const args = [ 'site', 'create', '--path', options.path, '--skip-browser', '--skip-log-details' ];

if ( options.siteId ) {
args.push( '--id', options.siteId );
}

if ( options.name ) {
args.push( '--name', options.name );
}

if ( options.wpVersion ) {
const wp = isWordPressDevVersion( options.wpVersion ) ? 'nightly' : options.wpVersion;
args.push( '--wp', wp );
}

if ( options.phpVersion ) {
args.push( '--php', options.phpVersion );
}

if ( options.runtime ) {
args.push( '--runtime', siteModeFromRuntime( options.runtime ) );
}

if ( options.fileAccess ) {
args.push( '--file-access', options.fileAccess );
}

if ( options.customDomain ) {
args.push( '--domain', options.customDomain );
}

if ( options.enableHttps ) {
args.push( '--https' );
}

if ( options.adminUsername ) {
args.push( '--admin-username', options.adminUsername );
}

if ( options.adminPassword ) {
args.push( '--admin-password', options.adminPassword );
}

if ( options.adminEmail ) {
args.push( '--admin-email', options.adminEmail );
}

if ( options.noStart ) {
args.push( '--no-start' );
}

return args;
}

function cleanupTempFile( filePath: string | undefined ): void {
if ( filePath && fs.existsSync( filePath ) ) {
try {
fs.unlinkSync( filePath );
} catch ( error ) {
console.error( 'Failed to clean up temp Blueprint file:', error );
}
}
}
83 changes: 4 additions & 79 deletions apps/studio/src/modules/cli/lib/cli-site-editor.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,18 @@
import { type SiteFileAccess } from '@studio/common/lib/site-file-access';
import { type SiteMode } from '@studio/common/lib/site-runtime';
import { SiteCommandLoggerAction } from '@studio/common/logger-actions';
import { buildSiteSetArgs, type EditSiteOptions } from '@studio/common/sites/edit';
import { z } from 'zod';
import { executeCliCommand } from './execute-command';

export type { EditSiteOptions };

const cliEventSchema = z.object( {
action: z.enum( SiteCommandLoggerAction ),
status: z.enum( [ 'inprogress', 'fail', 'success', 'warning' ] ),
message: z.string(),
} );

export interface EditSiteOptions {
path: string;
siteId: string;
name?: string;
domain?: string;
https?: boolean;
php?: string;
wp?: string;
runtime?: SiteMode;
fileAccess?: SiteFileAccess;
xdebug?: boolean;
adminUsername?: string;
adminPassword?: string;
adminEmail?: string;
debugLog?: boolean;
debugDisplay?: boolean;
}

export async function editSiteViaCli( options: EditSiteOptions ): Promise< void > {
const args = buildCliArgs( options );
const args = buildSiteSetArgs( options );
console.log( `[CLI Site Editor] Executing: studio ${ args.join( ' ' ) }` );

return new Promise( ( resolve, reject ) => {
Expand Down Expand Up @@ -60,61 +43,3 @@ export async function editSiteViaCli( options: EditSiteOptions ): Promise< void
} );
} );
}

function buildCliArgs( options: EditSiteOptions ): string[] {
const args = [ 'site', 'set', '--path', options.path ];

if ( options.name !== undefined ) {
args.push( '--name', options.name );
}

if ( options.domain !== undefined ) {
args.push( '--domain', options.domain );
}

if ( options.https !== undefined ) {
args.push( options.https ? '--https' : '--no-https' );
}

if ( options.php !== undefined ) {
args.push( '--php', options.php );
}

if ( options.wp !== undefined ) {
args.push( '--wp', options.wp );
}

if ( options.runtime !== undefined ) {
args.push( '--runtime', options.runtime );
}

if ( options.fileAccess !== undefined ) {
args.push( '--file-access', options.fileAccess );
}

if ( options.xdebug !== undefined ) {
args.push( options.xdebug ? '--xdebug' : '--no-xdebug' );
}

if ( options.adminUsername !== undefined ) {
args.push( '--admin-username', options.adminUsername );
}

if ( options.adminPassword !== undefined ) {
args.push( '--admin-password', options.adminPassword );
}

if ( options.adminEmail !== undefined ) {
args.push( '--admin-email', options.adminEmail );
}

if ( options.debugLog !== undefined ) {
args.push( options.debugLog ? '--debug-log' : '--no-debug-log' );
}

if ( options.debugDisplay !== undefined ) {
args.push( options.debugDisplay ? '--debug-display' : '--no-debug-display' );
}

return args;
}
Loading