136c0b2b4SAndreas Gohr#!/usr/bin/env php 236c0b2b4SAndreas Gohr<?php 336c0b2b4SAndreas Gohr 436c0b2b4SAndreas Gohruse dokuwiki\Extension\CLIPlugin; 536c0b2b4SAndreas Gohruse dokuwiki\Extension\PluginController; 65586e97bSAndreas Gohruse dokuwiki\plugin\dev\LangProcessor; 770316b84SAndreas Gohruse dokuwiki\plugin\dev\Skeletor; 81a23d1dbSAndreas Gohruse dokuwiki\plugin\dev\SVGIcon; 936c0b2b4SAndreas Gohruse splitbrain\phpcli\Exception as CliException; 1036c0b2b4SAndreas Gohruse splitbrain\phpcli\Options; 1136c0b2b4SAndreas Gohr 1236c0b2b4SAndreas Gohr/** 1336c0b2b4SAndreas Gohr * @license GPL2 1436c0b2b4SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 1536c0b2b4SAndreas Gohr */ 1636c0b2b4SAndreas Gohrclass cli_plugin_dev extends CLIPlugin 1736c0b2b4SAndreas Gohr{ 1836c0b2b4SAndreas Gohr /** 1936c0b2b4SAndreas Gohr * Register options and arguments on the given $options object 2036c0b2b4SAndreas Gohr * 2136c0b2b4SAndreas Gohr * @param Options $options 2236c0b2b4SAndreas Gohr * @return void 2336c0b2b4SAndreas Gohr */ 2436c0b2b4SAndreas Gohr protected function setup(Options $options) 2536c0b2b4SAndreas Gohr { 26f2576912SAndreas Gohr $options->useCompactHelp(); 2736c0b2b4SAndreas Gohr $options->setHelp( 28f2576912SAndreas Gohr "CLI to help with DokuWiki plugin and template development.\n\n" . 2936c0b2b4SAndreas Gohr "Run this script from within the extension's directory." 3036c0b2b4SAndreas Gohr ); 3136c0b2b4SAndreas Gohr 32fcb8165bSAndreas Gohr $options->registerCommand('init', 'Initialize a new plugin or template in the current directory.'); 33f2576912SAndreas Gohr $options->registerCommand('addTest', 'Add the testing framework files and a test. (_test/)'); 34f2576912SAndreas Gohr $options->registerArgument('test', 'Optional name of the new test. Defaults to the general test.', false, 35f2576912SAndreas Gohr 'addTest'); 36f2576912SAndreas Gohr $options->registerCommand('addConf', 'Add the configuration files. (conf/)'); 37f2576912SAndreas Gohr $options->registerCommand('addLang', 'Add the language files. (lang/)'); 38719b4841SAndreas Gohr $options->registerCommand('addAgents', 'Add an initial AGENTS.md file for guiding LLM coding agents'); 39*488499ccSAndreas Gohr $options->registerOption('claude', 'Symlink the AGENTS.md to CLAUDE.md for use with claude code', 'c', false, 'addAgents'); 4036c0b2b4SAndreas Gohr 41f2576912SAndreas Gohr $types = PluginController::PLUGIN_TYPES; 42f2576912SAndreas Gohr array_walk( 43f2576912SAndreas Gohr $types, 44f2576912SAndreas Gohr function (&$item) { 45f2576912SAndreas Gohr $item = $this->colors->wrap($item, $this->colors::C_BROWN); 46f2576912SAndreas Gohr } 4736c0b2b4SAndreas Gohr ); 4836c0b2b4SAndreas Gohr 49f2576912SAndreas Gohr $options->registerCommand('addComponent', 'Add a new plugin component.'); 50f2576912SAndreas Gohr $options->registerArgument('type', 'Type of the component. Needs to be one of ' . join(', ', $types), true, 51f2576912SAndreas Gohr 'addComponent'); 52f2576912SAndreas Gohr $options->registerArgument('name', 'Optional name of the component. Defaults to a base component.', false, 53f2576912SAndreas Gohr 'addComponent'); 54f2576912SAndreas Gohr 55f2576912SAndreas Gohr $options->registerCommand('deletedFiles', 'Create the list of deleted files based on the git history.'); 56f2576912SAndreas Gohr $options->registerCommand('rmObsolete', 'Delete obsolete files.'); 571a23d1dbSAndreas Gohr 581a23d1dbSAndreas Gohr $prefixes = array_keys(SVGIcon::SOURCES); 591a23d1dbSAndreas Gohr array_walk( 601a23d1dbSAndreas Gohr $prefixes, 611a23d1dbSAndreas Gohr function (&$item) { 621a23d1dbSAndreas Gohr $item = $this->colors->wrap($item, $this->colors::C_BROWN); 631a23d1dbSAndreas Gohr } 641a23d1dbSAndreas Gohr ); 651a23d1dbSAndreas Gohr 661a23d1dbSAndreas Gohr $options->registerCommand('downloadSvg', 'Download an SVG file from a known icon repository.'); 671a23d1dbSAndreas Gohr $options->registerArgument('prefix:name', 681a23d1dbSAndreas Gohr 'Colon-prefixed name of the icon. Available prefixes: ' . join(', ', $prefixes), true, 'downloadSvg'); 691a23d1dbSAndreas Gohr $options->registerArgument('output', 'File to save, defaults to <name>.svg in current dir', false, 701a23d1dbSAndreas Gohr 'downloadSvg'); 7192738407SAndreas Gohr $options->registerOption('keep-ns', 'Keep the SVG namespace. Use when the file is not inlined into HTML.', 'k', 7292738407SAndreas Gohr false, 'downloadSvg'); 731a23d1dbSAndreas Gohr 748f82d673SAndreas Gohr $options->registerCommand('cleanSvg', 'Clean a existing SVG files to reduce their file size.'); 758f82d673SAndreas Gohr $options->registerArgument('files...', 'The files to clean (will be overwritten)', true, 'cleanSvg'); 7692738407SAndreas Gohr $options->registerOption('keep-ns', 'Keep the SVG namespace. Use when the file is not inlined into HTML.', 'k', 7792738407SAndreas Gohr false, 'cleanSvg'); 785586e97bSAndreas Gohr 795586e97bSAndreas Gohr $options->registerCommand('cleanLang', 805586e97bSAndreas Gohr 'Clean language files from unused language strings. Detecting which strings are truly in use may ' . 815586e97bSAndreas Gohr 'not always correctly work. Use with caution.'); 82ec0a687bSAndreas Gohr 8357732a2dSAndreas Gohr $options->registerCommand( 8457732a2dSAndreas Gohr 'test', 8557732a2dSAndreas Gohr 'Run the unit tests for this extension. (calls phpunit using the proper config and group)' 8657732a2dSAndreas Gohr ); 8757732a2dSAndreas Gohr $options->registerOption( 8857732a2dSAndreas Gohr 'filter', 8957732a2dSAndreas Gohr 'Filter tests to run by a given string. (passed to phpunit)', 9057732a2dSAndreas Gohr null, 9157732a2dSAndreas Gohr true, 9257732a2dSAndreas Gohr 'test' 9357732a2dSAndreas Gohr ); 9457732a2dSAndreas Gohr $options->registerArgument('files...', 'The test files to run. Defaults to all.', false, 'test'); 95ec0a687bSAndreas Gohr 96ec0a687bSAndreas Gohr $options->registerCommand('check', 'Check for code style violations.'); 97ec0a687bSAndreas Gohr $options->registerArgument('files...', 'The files to check. Defaults to the whole extension.', false, 'check'); 98ec0a687bSAndreas Gohr 99ec0a687bSAndreas Gohr $options->registerCommand('fix', 'Fix code style violations and refactor outdated code.'); 100ec0a687bSAndreas Gohr $options->registerArgument('files...', 'The files to check. Defaults to the whole extension.', false, 'fix'); 10136c0b2b4SAndreas Gohr } 10236c0b2b4SAndreas Gohr 10336c0b2b4SAndreas Gohr /** @inheritDoc */ 10436c0b2b4SAndreas Gohr protected function main(Options $options) 10536c0b2b4SAndreas Gohr { 1061a23d1dbSAndreas Gohr $args = $options->getArgs(); 1071a23d1dbSAndreas Gohr 10836c0b2b4SAndreas Gohr switch ($options->getCmd()) { 10936c0b2b4SAndreas Gohr case 'init': 11036c0b2b4SAndreas Gohr return $this->cmdInit(); 11136c0b2b4SAndreas Gohr case 'addTest': 11236c0b2b4SAndreas Gohr $test = array_shift($args); 11336c0b2b4SAndreas Gohr return $this->cmdAddTest($test); 11436c0b2b4SAndreas Gohr case 'addConf': 11536c0b2b4SAndreas Gohr return $this->cmdAddConf(); 11636c0b2b4SAndreas Gohr case 'addLang': 11736c0b2b4SAndreas Gohr return $this->cmdAddLang(); 118719b4841SAndreas Gohr case 'addAgents': 119*488499ccSAndreas Gohr $claude = $options->getOpt('claude'); 120*488499ccSAndreas Gohr return $this->cmdAddAgents($claude); 12136c0b2b4SAndreas Gohr case 'addComponent': 12236c0b2b4SAndreas Gohr $type = array_shift($args); 12336c0b2b4SAndreas Gohr $component = array_shift($args); 12436c0b2b4SAndreas Gohr return $this->cmdAddComponent($type, $component); 12536c0b2b4SAndreas Gohr case 'deletedFiles': 12636c0b2b4SAndreas Gohr return $this->cmdDeletedFiles(); 127c5c85a97SAndreas Gohr case 'rmObsolete': 1281a23d1dbSAndreas Gohr return $this->cmdRmObsolete(); 1291a23d1dbSAndreas Gohr case 'downloadSvg': 1301a23d1dbSAndreas Gohr $ident = array_shift($args); 1311a23d1dbSAndreas Gohr $save = array_shift($args); 13270316b84SAndreas Gohr $keep = $options->getOpt('keep-ns'); 13392738407SAndreas Gohr return $this->cmdDownloadSVG($ident, $save, $keep); 1341a23d1dbSAndreas Gohr case 'cleanSvg': 13570316b84SAndreas Gohr $keep = $options->getOpt('keep-ns'); 1368f82d673SAndreas Gohr return $this->cmdCleanSVG($args, $keep); 1375586e97bSAndreas Gohr case 'cleanLang': 1385586e97bSAndreas Gohr return $this->cmdCleanLang(); 139ec0a687bSAndreas Gohr case 'test': 14057732a2dSAndreas Gohr $filter = $options->getOpt('filter'); 14157732a2dSAndreas Gohr return $this->cmdTest($filter, $args); 142ec0a687bSAndreas Gohr case 'check': 143ec0a687bSAndreas Gohr return $this->cmdCheck($args); 144ec0a687bSAndreas Gohr case 'fix': 145ec0a687bSAndreas Gohr return $this->cmdFix(); 14636c0b2b4SAndreas Gohr default: 1471a23d1dbSAndreas Gohr $this->error('Unknown command'); 14836c0b2b4SAndreas Gohr echo $options->help(); 14936c0b2b4SAndreas Gohr return 0; 15036c0b2b4SAndreas Gohr } 15136c0b2b4SAndreas Gohr } 15236c0b2b4SAndreas Gohr 15336c0b2b4SAndreas Gohr /** 15436c0b2b4SAndreas Gohr * Get the extension name from the current working directory 15536c0b2b4SAndreas Gohr * 15636c0b2b4SAndreas Gohr * @throws CliException if something's wrong 15736c0b2b4SAndreas Gohr * @param string $dir 15836c0b2b4SAndreas Gohr * @return string[] name, type 15936c0b2b4SAndreas Gohr */ 16036c0b2b4SAndreas Gohr protected function getTypedNameFromDir($dir) 16136c0b2b4SAndreas Gohr { 16236c0b2b4SAndreas Gohr $pdir = fullpath(DOKU_PLUGIN); 16336c0b2b4SAndreas Gohr $tdir = fullpath(tpl_incdir() . '../'); 16436c0b2b4SAndreas Gohr 16536c0b2b4SAndreas Gohr if (strpos($dir, $pdir) === 0) { 16636c0b2b4SAndreas Gohr $ldir = substr($dir, strlen($pdir)); 16736c0b2b4SAndreas Gohr $type = 'plugin'; 16836c0b2b4SAndreas Gohr } elseif (strpos($dir, $tdir) === 0) { 16936c0b2b4SAndreas Gohr $ldir = substr($dir, strlen($tdir)); 17036c0b2b4SAndreas Gohr $type = 'template'; 17136c0b2b4SAndreas Gohr } else { 17236c0b2b4SAndreas Gohr throw new CliException('Current directory needs to be in plugin or template directory'); 17336c0b2b4SAndreas Gohr } 17436c0b2b4SAndreas Gohr 17536c0b2b4SAndreas Gohr $ldir = trim($ldir, '/'); 17636c0b2b4SAndreas Gohr 17736c0b2b4SAndreas Gohr if (strpos($ldir, '/') !== false) { 17836c0b2b4SAndreas Gohr throw new CliException('Current directory has to be main extension directory'); 17936c0b2b4SAndreas Gohr } 18036c0b2b4SAndreas Gohr 18136c0b2b4SAndreas Gohr return [$ldir, $type]; 18236c0b2b4SAndreas Gohr } 18336c0b2b4SAndreas Gohr 18436c0b2b4SAndreas Gohr /** 18536c0b2b4SAndreas Gohr * Interactively ask for a value from the user 18636c0b2b4SAndreas Gohr * 18736c0b2b4SAndreas Gohr * @param string $prompt 18836c0b2b4SAndreas Gohr * @param bool $cache cache given value for next time? 18936c0b2b4SAndreas Gohr * @return string 19036c0b2b4SAndreas Gohr */ 19136c0b2b4SAndreas Gohr protected function readLine($prompt, $cache = false) 19236c0b2b4SAndreas Gohr { 19336c0b2b4SAndreas Gohr $value = ''; 19436c0b2b4SAndreas Gohr $default = ''; 19536c0b2b4SAndreas Gohr $cachename = getCacheName($prompt, '.readline'); 19636c0b2b4SAndreas Gohr if ($cache && file_exists($cachename)) { 19736c0b2b4SAndreas Gohr $default = file_get_contents($cachename); 19836c0b2b4SAndreas Gohr } 19936c0b2b4SAndreas Gohr 20036c0b2b4SAndreas Gohr while ($value === '') { 20136c0b2b4SAndreas Gohr echo $prompt; 20236c0b2b4SAndreas Gohr if ($default) echo ' [' . $default . ']'; 20336c0b2b4SAndreas Gohr echo ': '; 20436c0b2b4SAndreas Gohr 20536c0b2b4SAndreas Gohr $fh = fopen('php://stdin', 'r'); 20636c0b2b4SAndreas Gohr $value = trim(fgets($fh)); 20736c0b2b4SAndreas Gohr fclose($fh); 20836c0b2b4SAndreas Gohr 20936c0b2b4SAndreas Gohr if ($value === '') $value = $default; 21036c0b2b4SAndreas Gohr } 21136c0b2b4SAndreas Gohr 21236c0b2b4SAndreas Gohr if ($cache) { 21336c0b2b4SAndreas Gohr file_put_contents($cachename, $value); 21436c0b2b4SAndreas Gohr } 21536c0b2b4SAndreas Gohr 21636c0b2b4SAndreas Gohr return $value; 21736c0b2b4SAndreas Gohr } 21836c0b2b4SAndreas Gohr 21936c0b2b4SAndreas Gohr /** 22070316b84SAndreas Gohr * Create the given files with their given content 22136c0b2b4SAndreas Gohr * 22270316b84SAndreas Gohr * Ignores all files that already exist 22370316b84SAndreas Gohr * 22470316b84SAndreas Gohr * @param array $files A File array as created by Skeletor::getFiles() 22536c0b2b4SAndreas Gohr */ 226fcb8165bSAndreas Gohr protected function createFiles($files) 227fcb8165bSAndreas Gohr { 22870316b84SAndreas Gohr foreach ($files as $path => $content) { 22970316b84SAndreas Gohr if (file_exists($path)) { 23070316b84SAndreas Gohr $this->error($path . ' already exists'); 23170316b84SAndreas Gohr continue; 23236c0b2b4SAndreas Gohr } 23336c0b2b4SAndreas Gohr 23470316b84SAndreas Gohr io_makeFileDir($path); 23570316b84SAndreas Gohr file_put_contents($path, $content); 23670316b84SAndreas Gohr $this->success($path . ' created'); 23736c0b2b4SAndreas Gohr } 23836c0b2b4SAndreas Gohr } 23936c0b2b4SAndreas Gohr 24036c0b2b4SAndreas Gohr /** 241c5c85a97SAndreas Gohr * Delete the given file if it exists 242c5c85a97SAndreas Gohr * 243c5c85a97SAndreas Gohr * @param string $file 244c5c85a97SAndreas Gohr */ 245c5c85a97SAndreas Gohr protected function deleteFile($file) 246c5c85a97SAndreas Gohr { 247c5c85a97SAndreas Gohr if (!file_exists($file)) return; 248c5c85a97SAndreas Gohr if (@unlink($file)) { 249c5c85a97SAndreas Gohr $this->success('Delete ' . $file); 250c5c85a97SAndreas Gohr } 251c5c85a97SAndreas Gohr } 252c5c85a97SAndreas Gohr 253c5c85a97SAndreas Gohr /** 254c5c85a97SAndreas Gohr * Run git with the given arguments and return the output 255c5c85a97SAndreas Gohr * 256c5c85a97SAndreas Gohr * @throws CliException when the command can't be run 257c5c85a97SAndreas Gohr * @param string ...$args 258c5c85a97SAndreas Gohr * @return string[] 259c5c85a97SAndreas Gohr */ 260c5c85a97SAndreas Gohr protected function git(...$args) 261c5c85a97SAndreas Gohr { 262c5c85a97SAndreas Gohr $args = array_map('escapeshellarg', $args); 263c5c85a97SAndreas Gohr $cmd = 'git ' . join(' ', $args); 264c5c85a97SAndreas Gohr $output = []; 265c5c85a97SAndreas Gohr $result = 0; 266c5c85a97SAndreas Gohr 267c5c85a97SAndreas Gohr $this->info($cmd); 268c5c85a97SAndreas Gohr $last = exec($cmd, $output, $result); 269c5c85a97SAndreas Gohr if ($last === false || $result !== 0) { 270c5c85a97SAndreas Gohr throw new CliException('Running git failed'); 271c5c85a97SAndreas Gohr } 272c5c85a97SAndreas Gohr 273c5c85a97SAndreas Gohr return $output; 274c5c85a97SAndreas Gohr } 275c5c85a97SAndreas Gohr 276c5c85a97SAndreas Gohr // region Commands 277c5c85a97SAndreas Gohr 278c5c85a97SAndreas Gohr /** 27936c0b2b4SAndreas Gohr * Intialize the current directory as a plugin or template 28036c0b2b4SAndreas Gohr * 28136c0b2b4SAndreas Gohr * @return int 28236c0b2b4SAndreas Gohr */ 28336c0b2b4SAndreas Gohr protected function cmdInit() 28436c0b2b4SAndreas Gohr { 28536c0b2b4SAndreas Gohr $dir = fullpath(getcwd()); 28636c0b2b4SAndreas Gohr if ((new FilesystemIterator($dir))->valid()) { 287fcb8165bSAndreas Gohr // existing directory, initialize from info file 288fcb8165bSAndreas Gohr $skeletor = Skeletor::fromDir($dir); 289fcb8165bSAndreas Gohr } else { 290fcb8165bSAndreas Gohr // new directory, ask for info 29170316b84SAndreas Gohr [$base, $type] = $this->getTypedNameFromDir($dir); 29236c0b2b4SAndreas Gohr $user = $this->readLine('Your Name', true); 29336c0b2b4SAndreas Gohr $mail = $this->readLine('Your E-Mail', true); 29436c0b2b4SAndreas Gohr $desc = $this->readLine('Short description'); 29570316b84SAndreas Gohr $skeletor = new Skeletor($type, $base, $desc, $user, $mail); 296fcb8165bSAndreas Gohr } 29770316b84SAndreas Gohr $skeletor->addBasics(); 29870316b84SAndreas Gohr $this->createFiles($skeletor->getFiles()); 29936c0b2b4SAndreas Gohr 300fcb8165bSAndreas Gohr if (!is_dir("$dir/.git")) { 3018b06c9ddSAndreas Gohr try { 3028b06c9ddSAndreas Gohr $this->git('init'); 3038b06c9ddSAndreas Gohr } catch (CliException $e) { 3048b06c9ddSAndreas Gohr $this->error($e->getMessage()); 3058b06c9ddSAndreas Gohr } 306fcb8165bSAndreas Gohr } 3078b06c9ddSAndreas Gohr 30836c0b2b4SAndreas Gohr return 0; 30936c0b2b4SAndreas Gohr } 31036c0b2b4SAndreas Gohr 31136c0b2b4SAndreas Gohr /** 31236c0b2b4SAndreas Gohr * Add test framework 31336c0b2b4SAndreas Gohr * 31436c0b2b4SAndreas Gohr * @param string $test Name of the Test to add 31536c0b2b4SAndreas Gohr * @return int 31636c0b2b4SAndreas Gohr */ 31736c0b2b4SAndreas Gohr protected function cmdAddTest($test = '') 31836c0b2b4SAndreas Gohr { 31970316b84SAndreas Gohr $skeletor = Skeletor::fromDir(getcwd()); 32070316b84SAndreas Gohr $skeletor->addTest($test); 32170316b84SAndreas Gohr $this->createFiles($skeletor->getFiles()); 32236c0b2b4SAndreas Gohr return 0; 32336c0b2b4SAndreas Gohr } 32436c0b2b4SAndreas Gohr 32536c0b2b4SAndreas Gohr /** 32636c0b2b4SAndreas Gohr * Add configuration 32736c0b2b4SAndreas Gohr * 32836c0b2b4SAndreas Gohr * @return int 32936c0b2b4SAndreas Gohr */ 33036c0b2b4SAndreas Gohr protected function cmdAddConf() 33136c0b2b4SAndreas Gohr { 33270316b84SAndreas Gohr $skeletor = Skeletor::fromDir(getcwd()); 33370316b84SAndreas Gohr $skeletor->addConf(is_dir('lang')); 33470316b84SAndreas Gohr $this->createFiles($skeletor->getFiles()); 33536c0b2b4SAndreas Gohr return 0; 33636c0b2b4SAndreas Gohr } 33736c0b2b4SAndreas Gohr 33836c0b2b4SAndreas Gohr /** 33936c0b2b4SAndreas Gohr * Add language 34036c0b2b4SAndreas Gohr * 34136c0b2b4SAndreas Gohr * @return int 34236c0b2b4SAndreas Gohr */ 34336c0b2b4SAndreas Gohr protected function cmdAddLang() 34436c0b2b4SAndreas Gohr { 34570316b84SAndreas Gohr $skeletor = Skeletor::fromDir(getcwd()); 34670316b84SAndreas Gohr $skeletor->addLang(is_dir('conf')); 34770316b84SAndreas Gohr $this->createFiles($skeletor->getFiles()); 34836c0b2b4SAndreas Gohr return 0; 34936c0b2b4SAndreas Gohr } 35036c0b2b4SAndreas Gohr 35136c0b2b4SAndreas Gohr /** 352719b4841SAndreas Gohr * Add AGENTS.md 353719b4841SAndreas Gohr * 354719b4841SAndreas Gohr * @return int 355719b4841SAndreas Gohr */ 356*488499ccSAndreas Gohr protected function cmdAddAgents($claude) 357719b4841SAndreas Gohr { 358719b4841SAndreas Gohr $skeletor = Skeletor::fromDir(getcwd()); 359719b4841SAndreas Gohr $skeletor->addAgents(); 360719b4841SAndreas Gohr $this->createFiles($skeletor->getFiles()); 361*488499ccSAndreas Gohr if($claude && !file_exists('CLAUDE.md')) { 362*488499ccSAndreas Gohr symlink('AGENTS.md', 'CLAUDE.md') && $this->success('Created symlink CLAUDE.md -> AGENTS.md'); 363*488499ccSAndreas Gohr } 364719b4841SAndreas Gohr return 0; 365719b4841SAndreas Gohr } 366719b4841SAndreas Gohr 367719b4841SAndreas Gohr /** 36836c0b2b4SAndreas Gohr * Add another component to the plugin 36936c0b2b4SAndreas Gohr * 37036c0b2b4SAndreas Gohr * @param string $type 37136c0b2b4SAndreas Gohr * @param string $component 37236c0b2b4SAndreas Gohr */ 37336c0b2b4SAndreas Gohr protected function cmdAddComponent($type, $component = '') 37436c0b2b4SAndreas Gohr { 37570316b84SAndreas Gohr $skeletor = Skeletor::fromDir(getcwd()); 37670316b84SAndreas Gohr $skeletor->addComponent($type, $component); 37770316b84SAndreas Gohr $this->createFiles($skeletor->getFiles()); 37836c0b2b4SAndreas Gohr return 0; 37936c0b2b4SAndreas Gohr } 38036c0b2b4SAndreas Gohr 38136c0b2b4SAndreas Gohr /** 38236c0b2b4SAndreas Gohr * Generate a list of deleted files from git 38336c0b2b4SAndreas Gohr * 38436c0b2b4SAndreas Gohr * @link https://stackoverflow.com/a/6018049/172068 38536c0b2b4SAndreas Gohr */ 38636c0b2b4SAndreas Gohr protected function cmdDeletedFiles() 38736c0b2b4SAndreas Gohr { 3888b06c9ddSAndreas Gohr if (!is_dir('.git')) throw new CliException('This extension seems not to be managed by git'); 38936c0b2b4SAndreas Gohr 3908b06c9ddSAndreas Gohr $output = $this->git('log', '--no-renames', '--pretty=format:', '--name-only', '--diff-filter=D'); 39136c0b2b4SAndreas Gohr $output = array_map('trim', $output); 39236c0b2b4SAndreas Gohr $output = array_filter($output); 39336c0b2b4SAndreas Gohr $output = array_unique($output); 39436c0b2b4SAndreas Gohr $output = array_filter($output, function ($item) { 39536c0b2b4SAndreas Gohr return !file_exists($item); 39636c0b2b4SAndreas Gohr }); 39736c0b2b4SAndreas Gohr sort($output); 39836c0b2b4SAndreas Gohr 39936c0b2b4SAndreas Gohr if (!count($output)) { 40036c0b2b4SAndreas Gohr $this->info('No deleted files found'); 40136c0b2b4SAndreas Gohr return 0; 40236c0b2b4SAndreas Gohr } 40336c0b2b4SAndreas Gohr 40436c0b2b4SAndreas Gohr $content = "# This is a list of files that were present in previous releases\n" . 40536c0b2b4SAndreas Gohr "# but were removed later. They should not exist in your installation.\n" . 40636c0b2b4SAndreas Gohr join("\n", $output) . "\n"; 40736c0b2b4SAndreas Gohr 40836c0b2b4SAndreas Gohr file_put_contents('deleted.files', $content); 40936c0b2b4SAndreas Gohr $this->success('written deleted.files'); 41036c0b2b4SAndreas Gohr return 0; 41136c0b2b4SAndreas Gohr } 4128b06c9ddSAndreas Gohr 4138b06c9ddSAndreas Gohr /** 414c5c85a97SAndreas Gohr * Remove files that shouldn't be here anymore 4158b06c9ddSAndreas Gohr */ 4161a23d1dbSAndreas Gohr protected function cmdRmObsolete() 4178b06c9ddSAndreas Gohr { 418c5c85a97SAndreas Gohr $this->deleteFile('_test/general.test.php'); 419c5c85a97SAndreas Gohr $this->deleteFile('.travis.yml'); 42053bec4caSAndreas Gohr $this->deleteFile('.github/workflows/phpTestLinux.yml'); 4218b06c9ddSAndreas Gohr 422c5c85a97SAndreas Gohr return 0; 4238b06c9ddSAndreas Gohr } 4248b06c9ddSAndreas Gohr 4251a23d1dbSAndreas Gohr /** 4261a23d1dbSAndreas Gohr * Download a remote icon 4271a23d1dbSAndreas Gohr * 4281a23d1dbSAndreas Gohr * @param string $ident 4291a23d1dbSAndreas Gohr * @param string $save 43092738407SAndreas Gohr * @param bool $keep 4311a23d1dbSAndreas Gohr * @return int 4321a23d1dbSAndreas Gohr * @throws Exception 4331a23d1dbSAndreas Gohr */ 43492738407SAndreas Gohr protected function cmdDownloadSVG($ident, $save = '', $keep = false) 4351a23d1dbSAndreas Gohr { 4361a23d1dbSAndreas Gohr $svg = new SVGIcon($this); 43792738407SAndreas Gohr $svg->keepNamespace($keep); 4381a23d1dbSAndreas Gohr return (int)$svg->downloadRemoteIcon($ident, $save); 4391a23d1dbSAndreas Gohr } 4401a23d1dbSAndreas Gohr 4411a23d1dbSAndreas Gohr /** 4428f82d673SAndreas Gohr * @param string[] $files 44392738407SAndreas Gohr * @param bool $keep 4441a23d1dbSAndreas Gohr * @return int 4451a23d1dbSAndreas Gohr * @throws Exception 4461a23d1dbSAndreas Gohr */ 4478f82d673SAndreas Gohr protected function cmdCleanSVG($files, $keep = false) 4481a23d1dbSAndreas Gohr { 4491a23d1dbSAndreas Gohr $svg = new SVGIcon($this); 45092738407SAndreas Gohr $svg->keepNamespace($keep); 4518f82d673SAndreas Gohr 4528f82d673SAndreas Gohr $ok = true; 4538f82d673SAndreas Gohr foreach ($files as $file) { 4548f82d673SAndreas Gohr $ok = $ok && $svg->cleanSVGFile($file); 4558f82d673SAndreas Gohr } 4568f82d673SAndreas Gohr return (int)$ok; 4571a23d1dbSAndreas Gohr } 4581a23d1dbSAndreas Gohr 4595586e97bSAndreas Gohr /** 4605586e97bSAndreas Gohr * @return int 4615586e97bSAndreas Gohr */ 4625586e97bSAndreas Gohr protected function cmdCleanLang() 4635586e97bSAndreas Gohr { 4645586e97bSAndreas Gohr $lp = new LangProcessor($this); 4655586e97bSAndreas Gohr 4665586e97bSAndreas Gohr $files = glob('./lang/*/lang.php'); 4675b2e8f12SAndreas Gohr foreach ($files as $file) { 4685b2e8f12SAndreas Gohr $lp->processLangFile($file); 4695b2e8f12SAndreas Gohr } 4705b2e8f12SAndreas Gohr 4715b2e8f12SAndreas Gohr $files = glob('./lang/*/settings.php'); 4725586e97bSAndreas Gohr foreach ($files as $file) { 473f4f76afdSAndreas Gohr $lp->processSettingsFile($file); 4745586e97bSAndreas Gohr } 4755586e97bSAndreas Gohr 4765586e97bSAndreas Gohr return 0; 4775586e97bSAndreas Gohr } 4785586e97bSAndreas Gohr 479ec0a687bSAndreas Gohr /** 48057732a2dSAndreas Gohr * Run the unit tests for this extension 48157732a2dSAndreas Gohr * 48257732a2dSAndreas Gohr * @param string $filter Optional filter string for phpunit 48357732a2dSAndreas Gohr * @param string[] $args Additional arguments to pass to phpunit (files) 484ec0a687bSAndreas Gohr * @return int 485ec0a687bSAndreas Gohr */ 48657732a2dSAndreas Gohr protected function cmdTest($filter = '', $args = []) 487ec0a687bSAndreas Gohr { 488ec0a687bSAndreas Gohr $dir = fullpath(getcwd()); 489ec0a687bSAndreas Gohr [$base, $type] = $this->getTypedNameFromDir($dir); 490ec0a687bSAndreas Gohr 491ec0a687bSAndreas Gohr if ($this->colors->isEnabled()) { 492ec0a687bSAndreas Gohr $colors = 'always'; 493ec0a687bSAndreas Gohr } else { 494ec0a687bSAndreas Gohr $colors = 'never'; 495ec0a687bSAndreas Gohr } 496ec0a687bSAndreas Gohr 49757732a2dSAndreas Gohr $bin = fullpath(__DIR__ . '/../../../_test/vendor/bin/phpunit');; 49857732a2dSAndreas Gohr if (!file_exists($bin)) { 49957732a2dSAndreas Gohr $this->error('Testing framework not found. Please run "composer install" in the _test/ directory first.'); 50057732a2dSAndreas Gohr return 1; 50157732a2dSAndreas Gohr } 50257732a2dSAndreas Gohr 50357732a2dSAndreas Gohr $runArgs = [ 50457732a2dSAndreas Gohr $bin, 505ec0a687bSAndreas Gohr '--verbose', 506ec0a687bSAndreas Gohr "--colors=$colors", 507ec0a687bSAndreas Gohr '--configuration', fullpath(__DIR__ . '/../../../_test/phpunit.xml'), 508ec0a687bSAndreas Gohr '--group', $type . '_' . $base, 509ec0a687bSAndreas Gohr ]; 51057732a2dSAndreas Gohr if ($filter) { 51157732a2dSAndreas Gohr $runArgs[] = '--filter'; 51257732a2dSAndreas Gohr $runArgs[] = $filter; 51357732a2dSAndreas Gohr } 51457732a2dSAndreas Gohr 51557732a2dSAndreas Gohr $runArgs = array_merge($runArgs, $args); 51657732a2dSAndreas Gohr $cmd = join(' ', array_map('escapeshellarg', $runArgs)); 517ec0a687bSAndreas Gohr $this->info("Running $cmd"); 518ec0a687bSAndreas Gohr 519ec0a687bSAndreas Gohr $result = 0; 520ec0a687bSAndreas Gohr passthru($cmd, $result); 521ec0a687bSAndreas Gohr return $result; 522ec0a687bSAndreas Gohr } 523ec0a687bSAndreas Gohr 524ec0a687bSAndreas Gohr /** 525ec0a687bSAndreas Gohr * @return int 526ec0a687bSAndreas Gohr */ 527ec0a687bSAndreas Gohr protected function cmdCheck($files = []) 528ec0a687bSAndreas Gohr { 529ec0a687bSAndreas Gohr $dir = fullpath(getcwd()); 530ec0a687bSAndreas Gohr 531ec0a687bSAndreas Gohr $args = [ 532ec0a687bSAndreas Gohr fullpath(__DIR__ . '/../../../_test/vendor/bin/phpcs'), 533ec0a687bSAndreas Gohr '--standard=' . fullpath(__DIR__ . '/../../../_test/phpcs.xml'), 534ec0a687bSAndreas Gohr ($this->colors->isEnabled()) ? '--colors' : '--no-colors', 535ec0a687bSAndreas Gohr '--', 536ec0a687bSAndreas Gohr ]; 537ec0a687bSAndreas Gohr 538ec0a687bSAndreas Gohr if ($files) { 539ec0a687bSAndreas Gohr $args = array_merge($args, $files); 540ec0a687bSAndreas Gohr } else { 541ec0a687bSAndreas Gohr $args[] = fullpath($dir); 542ec0a687bSAndreas Gohr } 543ec0a687bSAndreas Gohr 544ec0a687bSAndreas Gohr $cmd = join(' ', array_map('escapeshellarg', $args)); 545ec0a687bSAndreas Gohr $this->info("Running $cmd"); 546ec0a687bSAndreas Gohr 547ec0a687bSAndreas Gohr $result = 0; 548ec0a687bSAndreas Gohr passthru($cmd, $result); 549ec0a687bSAndreas Gohr return $result; 550ec0a687bSAndreas Gohr } 551ec0a687bSAndreas Gohr 552ec0a687bSAndreas Gohr /** 553ec0a687bSAndreas Gohr * @return int 554ec0a687bSAndreas Gohr */ 555ec0a687bSAndreas Gohr protected function cmdFix($files = []) 556ec0a687bSAndreas Gohr { 557ec0a687bSAndreas Gohr $dir = fullpath(getcwd()); 558ec0a687bSAndreas Gohr 559ec0a687bSAndreas Gohr // first run rector to refactor outdated code 560ec0a687bSAndreas Gohr $args = [ 561ec0a687bSAndreas Gohr fullpath(__DIR__ . '/../../../_test/vendor/bin/rector'), 562ec0a687bSAndreas Gohr ($this->colors->isEnabled()) ? '--ansi' : '--no-ansi', 563ec0a687bSAndreas Gohr '--config=' . fullpath(__DIR__ . '/../../../_test/rector.php'), 564ec0a687bSAndreas Gohr '--no-diffs', 565ec0a687bSAndreas Gohr 'process', 566ec0a687bSAndreas Gohr ]; 567ec0a687bSAndreas Gohr 568ec0a687bSAndreas Gohr if ($files) { 569ec0a687bSAndreas Gohr $args = array_merge($args, $files); 570ec0a687bSAndreas Gohr } else { 571ec0a687bSAndreas Gohr $args[] = fullpath($dir); 572ec0a687bSAndreas Gohr } 573ec0a687bSAndreas Gohr 574ec0a687bSAndreas Gohr $cmd = join(' ', array_map('escapeshellarg', $args)); 575ec0a687bSAndreas Gohr $this->info("Running $cmd"); 576ec0a687bSAndreas Gohr 577ec0a687bSAndreas Gohr $result = 0; 578ec0a687bSAndreas Gohr passthru($cmd, $result); 579ec0a687bSAndreas Gohr if ($result !== 0) return $result; 580ec0a687bSAndreas Gohr 581ec0a687bSAndreas Gohr // now run phpcbf to clean up code style 582ec0a687bSAndreas Gohr $args = [ 583ec0a687bSAndreas Gohr fullpath(__DIR__ . '/../../../_test/vendor/bin/phpcbf'), 584ec0a687bSAndreas Gohr '--standard=' . fullpath(__DIR__ . '/../../../_test/phpcs.xml'), 585ec0a687bSAndreas Gohr ($this->colors->isEnabled()) ? '--colors' : '--no-colors', 586ec0a687bSAndreas Gohr '--', 587ec0a687bSAndreas Gohr ]; 588ec0a687bSAndreas Gohr 589ec0a687bSAndreas Gohr if ($files) { 590ec0a687bSAndreas Gohr $args = array_merge($args, $files); 591ec0a687bSAndreas Gohr } else { 592ec0a687bSAndreas Gohr $args[] = fullpath($dir); 593ec0a687bSAndreas Gohr } 594ec0a687bSAndreas Gohr 595ec0a687bSAndreas Gohr $cmd = join(' ', array_map('escapeshellarg', $args)); 596ec0a687bSAndreas Gohr $this->info("Running $cmd"); 597ec0a687bSAndreas Gohr 598ec0a687bSAndreas Gohr $result = 0; 599ec0a687bSAndreas Gohr passthru($cmd, $result); 600ec0a687bSAndreas Gohr return $result; 601ec0a687bSAndreas Gohr } 602ec0a687bSAndreas Gohr 603c5c85a97SAndreas Gohr //endregion 60436c0b2b4SAndreas Gohr} 605