diff --git a/README.md b/README.md index e1d52a7..1c064b9 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ a set of sample data and behaviour. ## Usage -Simply running `dev/build` will take care of most sample data setup. +Simply running `sake db:build` will take care of most sample data setup. In order to use any of the optional test behaviour targeted at modules, install the module and remove the `_manifest_exclude` file from the relevant folder. @@ -26,7 +26,7 @@ For example, to test the tagfield module, remove the `frameworktest/code/tagfiel The module creates some default pages for different CMS behaviours. The CMS is intended to be perform well with a couple of thousand pages. If you want to test the CMS behaviour for a large and nested tree, -the module includes a simple generator task: `dev/tasks/FTPageMakerTask`. +the module includes a simple generator task: `sake tasks:FTPageMakerTask`. It will create 3^5 pages by default, so takes a while to run through. ## Configuring the amount of data @@ -35,9 +35,9 @@ Both `FTPageMagerTask` and `FTFileMakerTask` allow the amount of generated conte To do this, pass a comma-seprarated list of integers representing the amount of records to create at each depth. -`$ vendor/bin/sake dev/tasks/FTPageMakerTask pageCounts=10,200,5,5` +`$ vendor/bin/sake tasks:FTPageMakerTask --pageCounts=10,200,5,5` -`$ vendor/bin/sake dev/tasks/FTFileMakerTask fileCounts=5,300,55,5 folderCounts=1,5,5,5` +`$ vendor/bin/sake tasks:FTFileMakerTask --fileCounts=5,300,55,5 --folderCounts=1,5,5,5` ## Guaranteed unique images @@ -64,8 +64,8 @@ Usage: ``` # Generate some sample files to associate with blocks -sake dev/tasks/FTFileMakerTask -sake dev/tasks/FTPageMakerTask withBlocks=true +sake tasks:FTFileMakerTask +sake tasks:FTPageMakerTask withBlocks=true ``` ## Requirements diff --git a/_config/extensions.yml b/_config/extensions.yml index ee8e25f..6828e29 100644 --- a/_config/extensions.yml +++ b/_config/extensions.yml @@ -28,9 +28,9 @@ SilverStripe\FrameworkTest\Fields\NestedGridField\NonRelationalData: extensions: - SilverStripe\FrameworkTest\Extension\TestDataObjectExtension -SilverStripe\ORM\DatabaseAdmin: +SilverStripe\Dev\Command\DbBuild: extensions: - - SilverStripe\FrameworkTest\GridFieldArbitraryData\DatabaseBuildExtension + - SilverStripe\FrameworkTest\GridFieldArbitraryData\DbBuildExtension --- Only: @@ -38,7 +38,7 @@ Only: --- SilverStripe\TestSession\TestSessionEnvironment: extensions: - - SilverStripe\FrameworkTest\GridFieldArbitraryData\DatabaseBuildExtension + - SilverStripe\FrameworkTest\GridFieldArbitraryData\DbBuildExtension --- Only: diff --git a/code/GridFieldArbitraryData/DatabaseBuildExtension.php b/code/GridFieldArbitraryData/DbBuildExtension.php similarity index 52% rename from code/GridFieldArbitraryData/DatabaseBuildExtension.php rename to code/GridFieldArbitraryData/DbBuildExtension.php index 1267045..bec63d8 100644 --- a/code/GridFieldArbitraryData/DatabaseBuildExtension.php +++ b/code/GridFieldArbitraryData/DbBuildExtension.php @@ -4,7 +4,8 @@ use SilverStripe\Control\Director; use SilverStripe\Core\Extension; -use SilverStripe\ORM\DatabaseAdmin; +use SilverStripe\Dev\Command\DbBuild; +use SilverStripe\PolyExecution\PolyOutput; use SilverStripe\ORM\DataObject; use SilverStripe\ORM\DB; use SilverStripe\ORM\FieldType\DBDatetime; @@ -13,87 +14,66 @@ /** * Builds the table and adds default records for the ArbitraryDataModel. */ -class DatabaseBuildExtension extends Extension +class DbBuildExtension extends Extension { /** * This extension hook is on TestSessionEnvironment, which is used by behat but not by phpunit. - * For whatever reason, behat doesn't use dev/build, so we can't rely on the below onAfterbuild + * For whatever reason, behat doesn't build the db, so we can't rely on the below onAfterbuild * being run in that scenario. */ protected function onAfterStartTestSession() { - $this->buildTable(true); + $output = PolyOutput::create( + Director::is_cli() ? PolyOutput::FORMAT_ANSI : PolyOutput::FORMAT_HTML, + PolyOutput::VERBOSITY_QUIET + ); + $output->startList(); + $this->buildTable($output); + $output->stopList(); $this->populateData(); } /** - * This extension hook is on DatabaseAdmin, after dev/build has finished building the database. + * This extension hook is on DbBuild, after building the database. */ - protected function onAfterBuild(bool $quiet, bool $populate, bool $testMode): void + protected function onAfterBuild(PolyOutput $output, bool $populate, bool $testMode): void { if ($testMode) { return; } - if (!$quiet) { - if (Director::is_cli()) { - echo "\nCREATING TABLE FOR FRAMEWORKTEST ARBITRARY DATA\n\n"; - } else { - echo "\n

Creating table for frameworktest arbitrary data

'; - } - + $output->writeln('Creating table for frameworktest arbitrary data'); + $output->startList(); + $this->buildTable($output); + $output->stopList(); if ($populate) { - if (!$quiet) { - if (Director::is_cli()) { - echo "\nCREATING DATABASE RECORDS FOR FRAMEWORKTEST ARBITRARY DATA\n\n"; - } else { - echo "\n

Creating database records arbitrary data

'; - } - } - - if (!$quiet) { - echo (Director::is_cli()) ? "\n Frameworktest database build completed!\n\n" : '

Frameworktest database build completed!

'; + $output->stopList(); } + $output->writeln(['Frameworktest database build completed!', '']); } - private function buildTable(bool $quiet): void + private function buildTable(PolyOutput $output): void { $tableName = ArbitraryDataModel::TABLE_NAME; // Log data - if (!$quiet) { - $showRecordCounts = DatabaseAdmin::config()->get('show_record_counts'); - if ($showRecordCounts && DB::get_schema()->hasTable($tableName)) { - try { - $count = SQLSelect::create()->setFrom($tableName)->count(); - $countSuffix = " ($count records)"; - } catch (\Exception $e) { - $countSuffix = ' (error getting record count)'; - } - } else { - $countSuffix = ""; - } - - if (Director::is_cli()) { - echo " * $tableName$countSuffix\n"; - } else { - echo "
  • $tableName$countSuffix
  • \n"; + $showRecordCounts = DbBuild::config()->get('show_record_counts'); + if ($showRecordCounts && DB::get_schema()->hasTable($tableName)) { + try { + $count = SQLSelect::create()->setFrom($tableName)->count(); + $countSuffix = " ($count records)"; + } catch (\Exception $e) { + $countSuffix = ' (error getting record count)'; } + } else { + $countSuffix = ""; } + // We're adding one list item, but we need to do it this way for consistency + // with the rest of the db build output + $output->writeListItem($tableName . $countSuffix); // Get field schema $fields = [ diff --git a/code/tasks/FTFileMakerTask.php b/code/tasks/FTFileMakerTask.php index c23dfd1..3d3b006 100644 --- a/code/tasks/FTFileMakerTask.php +++ b/code/tasks/FTFileMakerTask.php @@ -11,6 +11,10 @@ use SilverStripe\Security\Security; use SilverStripe\Core\Path; use SilverStripe\Core\Manifest\ModuleResourceLoader; +use SilverStripe\PolyExecution\PolyOutput; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; /** * Creates sample folder and file structure, useful to test performance, @@ -24,10 +28,6 @@ * recursively delete any generated ones through the following bash command in `assets/`: * `find . -name '*Resampled*' -print0 | xargs -0 rm` * - * Parameters: - * - reset=1: Optionally truncate ALL files and folders in the database, plus delete - * the entire `assets/` directory. - * * * Configuration * @@ -58,7 +58,7 @@ * - 0 * * Flush and run: - * /dev/tasks/FTFileMakerTask?flush&reset=1 + * sake tasks:FTFileMakerTask --flush --reset * * @todo Automatically retrieve file listing from S3 * @todo Handle HTTP errors from S3 @@ -180,8 +180,6 @@ class FTFileMakerTask extends BuildTask 'video.m4v' => 'SilverStripe\Assets\File', ]; - protected $lineBreak = "\n
    "; - /** @var Member */ protected $anonymousMember = null; @@ -197,7 +195,7 @@ class FTFileMakerTask extends BuildTask */ protected $folderCounts = []; - public function run($request) + protected function execute(InputInterface $input, PolyOutput $output): int { set_time_limit(0); @@ -213,15 +211,11 @@ public function run($request) } Security::setCurrentUser($this->anonymousMember); - if (php_sapi_name() == "cli") { - $this->lineBreak = "\n"; - } - - if ($request->getVar('reset')) { - $this->reset(); + if ($input->getOption('reset')) { + $this->reset($output); } - $fileCounts = $request->getVar('fileCounts'); + $fileCounts = $input->getOption('fileCounts'); if ($fileCounts) { $counts = explode(',', $fileCounts ?? ''); $this->fileCounts = array_map(function ($int) { @@ -231,7 +225,7 @@ public function run($request) $this->fileCounts = FTFileMakerTask::config()->get('fileCountByDepth'); } - $folderCounts = $request->getVar('folderCounts'); + $folderCounts = $input->getOption('folderCounts'); if ($folderCounts) { $counts = explode(',', $folderCounts ?? ''); $this->folderCounts = array_map(function ($int) { @@ -241,26 +235,27 @@ public function run($request) $this->folderCounts = FTFileMakerTask::config()->get('folderCountByDepth'); } - echo "Downloading fixtures" . $this->lineBreak; - $fixtureFilePaths = $this->downloadFixtureFiles(); + $output->writeln('Downloading fixtures'); + $fixtureFilePaths = $this->downloadFixtureFiles($output); if (!FTFileMakerTask::config()->get('documentsOnly')) { - echo "Generate thumbnails" . $this->lineBreak; + $output->writeln('Generate thumbnails'); $this->generateThumbnails($fixtureFilePaths); } - echo "Generate files" . $this->lineBreak; + $output->writeln('Generate files'); $this->generateFiles($fixtureFilePaths); if (!FTFileMakerTask::config()->get('doPutProtectedFilesInPublicStore')) { - echo "Incorrectly putting protected files into public asset store on purpose" . $this->lineBreak; + $output->writeln('Incorrectly putting protected files into public asset store on purpose'); $this->putProtectedFilesInPublicAssetStore(); } + return Command::SUCCESS; } - protected function reset() + protected function reset(PolyOutput $output) { - echo "Resetting assets" . $this->lineBreak; + $output->writeln('Resetting assets'); DB::query('TRUNCATE "File"'); DB::query('TRUNCATE "File_Live"'); @@ -271,7 +266,7 @@ protected function reset() } } - protected function downloadFixtureFiles() + protected function downloadFixtureFiles(PolyOutput $output) { $client = new Client(['base_uri' => $this->fixtureFileBaseUrl]); @@ -293,7 +288,7 @@ protected function downloadFixtureFiles() $promises[$filename] = $client->getAsync($filename, [ 'sink' => $path ]); - echo "Downloading $url" . $this->lineBreak; + $output->writeln("Downloading $url"); } } @@ -497,4 +492,28 @@ protected function watermarkImage(string $stampPath, string $targetPath): ?strin return $targetPath; } + public function getOptions(): array + { + return [ + new InputOption( + 'reset', + null, + InputOption::VALUE_NONE, + 'Optionally truncate ALL files and folders in the database,' + . ' plus delete the entire `assets/` directory', + ), + new InputOption( + 'fileCounts', + null, + InputOption::VALUE_REQUIRED, + 'Comma separated string' + ), + new InputOption( + 'folderCounts', + null, + InputOption::VALUE_REQUIRED, + 'Comma separated string' + ), + ]; + } } diff --git a/code/tasks/FTPageMakerTask.php b/code/tasks/FTPageMakerTask.php index 4d8b276..3c96a68 100644 --- a/code/tasks/FTPageMakerTask.php +++ b/code/tasks/FTPageMakerTask.php @@ -8,7 +8,10 @@ use SilverStripe\ElementalFileBlock\Block\FileBlock; use SilverStripe\CMS\Model\SiteTree; use DNADesign\Elemental\Extensions\ElementalPageExtension; -use DNADesign\Elemental\Models\BaseElement; +use SilverStripe\PolyExecution\PolyOutput; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; /** * Creates sample page structure, useful to test tree performance, @@ -48,16 +51,16 @@ class FTPageMakerTask extends BuildTask 'SilverStripe\ElementalFileBlock\Block\FileBlock' => [FTPageMakerTask::class, 'generateFileBlock'], ]; - public function run($request) + protected function execute(InputInterface $input, PolyOutput $output): int { // Optionally add blocks - $withBlocks = (bool)$request->getVar('withBlocks'); + $withBlocks = (bool)$input->getOption('withBlocks'); if ($withBlocks && !class_exists('DNADesign\Elemental\Models\BaseElement')) { throw new \LogicException('withBlocks requested, but BaseElement class not found'); } // Allow pageCountByDepth to be passed as comma-separated value, e.g. pageCounts=5,100,1,1 - $pageCounts = $request->getVar('pageCounts'); + $pageCounts = $input->getOption('pageCounts'); if ($pageCounts) { $counts = explode(',', $pageCounts ?? ''); $this->pageCountByDepth = array_map(function ($int) { @@ -65,10 +68,11 @@ public function run($request) }, $counts ?? []); } - $this->generatePages(0, "", 0, $withBlocks); + $this->generatePages($output, 0, "", 0, $withBlocks); + return Command::SUCCESS; } - protected function generatePages($depth = 0, $prefix = "", $parentID = 0, $withBlocks = false) + protected function generatePages(PolyOutput $output, $depth = 0, $prefix = "", $parentID = 0, $withBlocks = false) { $maxDepth = count($this->pageCountByDepth ?? []); $pageCount = $this->pageCountByDepth[$depth]; @@ -84,10 +88,10 @@ protected function generatePages($depth = 0, $prefix = "", $parentID = 0, $withB $page->write(); $page->copyVersionToStage('Stage', 'Live'); - echo "Created '$page->Title' ($page->ClassName)\n"; + $output->writeln("Created '$page->Title' ($page->ClassName)"); if ($withBlocks) { - $this->generateBlocksForPage($page); + $this->generateBlocksForPage($output, $page); } $pageID = $page->ID; @@ -95,12 +99,12 @@ protected function generatePages($depth = 0, $prefix = "", $parentID = 0, $withB unset($page); if ($depth < $maxDepth-1) { - $this->generatePages($depth+1, $fullPrefix, $pageID, $withBlocks); + $this->generatePages($output, $depth+1, $fullPrefix, $pageID, $withBlocks); } } } - protected function generateBlocksForPage(Page $page) + protected function generateBlocksForPage(PolyOutput $output, Page $page) { $classes = array_filter($this->config()->get('block_generators') ?? [], function ($callable, $class) { return class_exists($class ?? ''); @@ -121,7 +125,7 @@ protected function generateBlocksForPage(Page $page) $block->publishRecursive(); } - echo sprintf(" Added '%s' block #%d to page #%d\n", $class, $block->ID, $page->ID); + $output->writeln(sprintf(" Added '%s' block #%d to page #%d", $class, $block->ID, $page->ID)); } } @@ -193,4 +197,21 @@ public static function generateBannerBlock(?SiteTree $page = null): BannerBlock return $block; } + public function getOptions(): array + { + return [ + new InputOption( + 'withBlocks', + null, + InputOption::VALUE_NONE, + 'Include elemental blocks on the page', + ), + new InputOption( + 'pageCounts', + null, + InputOption::VALUE_REQUIRED, + 'Comma separated string' + ), + ]; + } } diff --git a/code/tasks/FTPageTypeCreatorTask.php b/code/tasks/FTPageTypeCreatorTask.php index 541b822..96d7874 100644 --- a/code/tasks/FTPageTypeCreatorTask.php +++ b/code/tasks/FTPageTypeCreatorTask.php @@ -2,10 +2,13 @@ use SilverStripe\Dev\BuildTask; use Faker\Factory; -use SilverStripe\Control\HTTPRequest; use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\Finder\Finder; use SilverStripe\Core\Manifest\ModuleLoader; +use SilverStripe\PolyExecution\PolyOutput; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Filesystem\Exception\IOException; class FTPageTypeCreatorTask extends BuildTask @@ -36,12 +39,9 @@ public function __construct() $this->finder = new Finder(); } - /** - * @param HTTPRequest $request - */ - public function run($request) + protected function execute(InputInterface $input, PolyOutput $output): int { - $count = $request->getVar('count') ?: 20; + $count = $input->getOption('count'); $module = ModuleLoader::getModule('silverstripe/frameworktest'); $testPageDir = $module->getPath() . '/code/test-pages'; if (!$this->fs->exists($testPageDir)) { @@ -66,11 +66,19 @@ class_exists(basename($className ?? '', 'php')) $this->fs->dumpFile($filePath, $code); $created++; } catch (IOException $e) { - echo "Could not write to file $filePath. Got error: {$e->getMessage()}\n"; + $output->writeln("Could not write to file $filePath. Got error: {$e->getMessage()}"); die(); } - echo "Created page type $className\n"; + $output->writeln("Created page type $className"); } + return Command::SUCCESS; + } + + public function getOptions(): array + { + return [ + new InputOption('count', null, InputOption::VALUE_REQUIRED, 'Number of page types to create', 20), + ]; } private function getExistingClassNames($dir) @@ -107,5 +115,4 @@ class $className extends Page implements TestPageInterface PHP; return $code; } - -} \ No newline at end of file +}