Skip to content

Commit 0a823e0

Browse files
committed
Implement bundle path validation
1 parent cd6b9bb commit 0a823e0

File tree

2 files changed

+89
-5
lines changed

2 files changed

+89
-5
lines changed

components/Blueprints/BlueprintParser.php

Lines changed: 78 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,25 @@ public function parse( string $blueprint_string ): Blueprint {
7878
// Create the execution plan.
7979
$execution_plan = $this->createExecutionPlan( $blueprint_array );
8080

81+
// Collect errors from the plan.
82+
foreach ( $execution_plan as $step ) {
83+
if ( isset( $step['errors'] ) && count( $step['errors'] ) > 0 ) {
84+
$errors[$step['key']] = array_merge(
85+
$errors[$step['key']] ?? [],
86+
$step['errors']
87+
);
88+
} else {
89+
unset( $step['errors'] );
90+
}
91+
}
92+
8193
return new Blueprint(
8294
$blueprint_string,
8395
$blueprint_array,
8496
$php_version_constraint,
8597
$wp_version_constraint,
86-
$execution_plan
98+
$execution_plan,
99+
$errors
87100
);
88101
}
89102

@@ -313,17 +326,21 @@ private function createExecutionPlan( array $blueprint ): array {
313326

314327
private function buildConstantsStep( array $constants ): array {
315328
return [
329+
'key' => 'constants',
316330
'name' => 'defineConstants',
317331
'args' => [ 'constants' => $constants ],
332+
'errors' => [],
318333
];
319334
}
320335

321336
private function buildSiteOptionsStep( array $site_options ): array {
322337
// Ensure siteUrl is not included as per schema Omit<>
323338
unset( $site_options['siteUrl'] );
324339
return [
340+
'key' => 'siteOptions',
325341
'name' => 'setSiteOptions',
326342
'args' => [ 'options' => $site_options ],
343+
'errors' => [],
327344
];
328345
}
329346

@@ -339,14 +356,17 @@ private function buildMuPluginsStep( array $mu_plugins ): array {
339356
}
340357

341358
return [
359+
'key' => 'muPlugins',
342360
'name' => 'writeFiles',
343361
'args' => [ 'files' => $files ],
362+
'errors' => [],
344363
];
345364
}
346365

347366
private function buildThemeStep( $theme ): array {
348367
if ( is_string( $theme ) ) {
349368
$step = [
369+
'key' => 'themes',
350370
'name' => 'installTheme',
351371
'args' => [
352372
'source' => $theme,
@@ -357,6 +377,7 @@ private function buildThemeStep( $theme ): array {
357377
} elseif ( is_array( $theme ) && isset( $theme['source'] ) && is_string( $theme['source'] ) ) {
358378
// Pass through the raw definition for extensibility.
359379
$step = [
380+
'key' => 'themes',
360381
'name' => 'installTheme',
361382
'args' => [
362383
'source' => $theme['source'],
@@ -369,6 +390,8 @@ private function buildThemeStep( $theme ): array {
369390
throw new InvalidArgumentException( 'Invalid theme reference format in "themes" array.' );
370391
}
371392

393+
$error = $this->validateDataSource( $step['args']['source'], 'wp-content/themes/*' );
394+
$step['errors'] = $error ? [$error] : [];
372395
return $step;
373396
}
374397

@@ -377,73 +400,123 @@ private function buildActiveThemeStep( $theme ): array {
377400
$theme = [ 'source' => $theme ];
378401
}
379402
$theme['active'] = true;
380-
return $this->buildThemeStep( $theme );
403+
$step = $this->buildThemeStep( $theme );
404+
$step['key'] = 'activeTheme';
405+
return $step;
381406
}
382407

383408
private function buildPluginStep( $plugin ): array {
384409
if ( is_string( $plugin ) ) {
385410
$plugin = [ 'source' => $plugin ];
386411
}
387412

413+
$error = $this->validateDataSource( $plugin['source'], 'wp-content/plugins/*' );
388414
return [
415+
'key' => 'plugins',
389416
'name' => 'installPlugin',
390417
'args' => $plugin,
418+
'errors' => $error ? [$error] : [],
391419
];
392420
}
393421

394422
private function buildMediaStep( $media ): array {
423+
$errors = [];
424+
foreach ( $media as $media_def ) {
425+
$source = is_array( $media_def ) ? $media_def['source'] : $media_def;
426+
$error = $this->validateDataSource( $source, 'wp-content/uploads/*' );
427+
if ( $error ) {
428+
$errors[] = $error;
429+
}
430+
}
431+
395432
return [
433+
'key' => 'media',
396434
'name' => 'importMedia',
397435
'args' => [ 'media' => $media ],
436+
'errors' => $errors,
398437
];
399438
}
400439

401440
private function buildSiteLanguageStep( $site_language ): array {
402441
return [
442+
'key' => 'siteLanguage',
403443
'name' => 'setSiteLanguage',
404444
'args' => [ 'language' => $site_language ],
445+
'errors' => [],
405446
];
406447
}
407448

408449
private function buildRolesStep( $roles ): array {
409450
return [
451+
'key' => 'roles',
410452
'name' => 'createRoles',
411453
'args' => [ 'roles' => $roles ],
454+
'errors' => [],
412455
];
413456
}
414457

415458
private function buildUsersStep( $users ): array {
416459
return [
460+
'key' => 'users',
417461
'name' => 'createUsers',
418462
'args' => [ 'users' => $users ],
463+
'errors' => [],
419464
];
420465
}
421466

422467
private function buildPostTypesStep( $post_types ): array {
423468
return [
469+
'key' => 'postTypes',
424470
'name' => 'createPostTypes',
425471
'args' => [ 'postTypes' => $post_types ],
472+
'errors' => [],
426473
];
427474
}
428475

429476
private function buildContentStep( $content ): array {
430477
// @TODO: Consider splitting this into multiple importContent steps,
431478
// one per piece of content.
479+
$errors = [];
480+
foreach ( $content as $content_def ) {
481+
if ( 'posts' === $content_def['type'] ) {
482+
if ( isset( $content_def['source'] ) ) {
483+
$sources = $content_def['source'];
484+
} else {
485+
$sources = $content_def;
486+
}
487+
488+
if ( ! is_array( $sources ) ) {
489+
$sources = [ $sources ];
490+
}
491+
492+
foreach ( $sources as $source ) {
493+
$error = $this->validateDataSource( $source, 'wp-content/content/posts/*' );
494+
if ( $error ) {
495+
$errors[] = $error;
496+
}
497+
}
498+
}
499+
}
500+
432501
return [
502+
'key' => 'content',
433503
'name' => 'importContent',
434504
'args' => [ 'content' => $content ],
505+
'errors' => $errors,
435506
];
436507
}
437508

438509
private function buildAdditionalStepsAfterExecution( $step_data ): array {
439510
return [
511+
'key' => 'additionalStepsAfterExecution',
440512
'name' => $step_data['step'],
441513
'args' => $step_data,
514+
'errors' => [],
442515
];
443516
}
444517

445518
private function validateDataSource( string $source, string $allowed_pattern ): ?string {
446-
if ( strlen( $source ) === 0 ) {
519+
if ( 0 === strlen( $source ) ) {
447520
return 'Source must be a non-empty string.';
448521
}
449522

@@ -456,9 +529,9 @@ private function validateDataSource( string $source, string $allowed_pattern ):
456529
$byte_1 = $source[0];
457530
$byte_2 = $source[1] ?? null;
458531
if ( str_contains( $source, '/' ) ) {
459-
if ( $byte_1 === '/' ) {
532+
if ( '/' === $byte_1 ) {
460533
$source = substr( $source, 1 );
461-
} elseif ( $byte_1 === '.' && $byte_2 === '/' ) {
534+
} elseif ( '.' === $byte_1 && '/' === $byte_2 ) {
462535
$source = substr( $source, 2 );
463536
}
464537

components/Blueprints/bin/blueprint.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,17 @@ function handleInfoCommand( array $positionalArgs, array $options, array $comman
402402
$blueprint = $runner->parseBlueprint();
403403
$blueprint_array = $blueprint->getBlueprintArray();
404404

405+
if ( ! $blueprint->isValid() ) {
406+
echo "ERRORS FOUND:\n\n";
407+
foreach ( $blueprint->getErrors() as $step => $errors ) {
408+
echo sprintf(" %s:\n", $step);
409+
foreach ( $errors as $error ) {
410+
echo sprintf(" %s\n", $error);
411+
}
412+
}
413+
echo "\n";
414+
}
415+
405416
echo "BLUEPRINT:\n\n";
406417
foreach ( $blueprint_array as $key => $value ) {
407418
echo sprintf(" %s: %s\n", $key, json_encode( $value, JSON_UNESCAPED_SLASHES ) );

0 commit comments

Comments
 (0)