xref: /plugin/dev/cli.php (revision ec0a687b36b651ed2a5572d7eed877affca6dc6f)
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/)');
3836c0b2b4SAndreas Gohr
39f2576912SAndreas Gohr        $types = PluginController::PLUGIN_TYPES;
40f2576912SAndreas Gohr        array_walk(
41f2576912SAndreas Gohr            $types,
42f2576912SAndreas Gohr            function (&$item) {
43f2576912SAndreas Gohr                $item = $this->colors->wrap($item, $this->colors::C_BROWN);
44f2576912SAndreas Gohr            }
4536c0b2b4SAndreas Gohr        );
4636c0b2b4SAndreas Gohr
47f2576912SAndreas Gohr        $options->registerCommand('addComponent', 'Add a new plugin component.');
48f2576912SAndreas Gohr        $options->registerArgument('type', 'Type of the component. Needs to be one of ' . join(', ', $types), true,
49f2576912SAndreas Gohr            'addComponent');
50f2576912SAndreas Gohr        $options->registerArgument('name', 'Optional name of the component. Defaults to a base component.', false,
51f2576912SAndreas Gohr            'addComponent');
52f2576912SAndreas Gohr
53f2576912SAndreas Gohr        $options->registerCommand('deletedFiles', 'Create the list of deleted files based on the git history.');
54f2576912SAndreas Gohr        $options->registerCommand('rmObsolete', 'Delete obsolete files.');
551a23d1dbSAndreas Gohr
561a23d1dbSAndreas Gohr        $prefixes = array_keys(SVGIcon::SOURCES);
571a23d1dbSAndreas Gohr        array_walk(
581a23d1dbSAndreas Gohr            $prefixes,
591a23d1dbSAndreas Gohr            function (&$item) {
601a23d1dbSAndreas Gohr                $item = $this->colors->wrap($item, $this->colors::C_BROWN);
611a23d1dbSAndreas Gohr            }
621a23d1dbSAndreas Gohr        );
631a23d1dbSAndreas Gohr
641a23d1dbSAndreas Gohr        $options->registerCommand('downloadSvg', 'Download an SVG file from a known icon repository.');
651a23d1dbSAndreas Gohr        $options->registerArgument('prefix:name',
661a23d1dbSAndreas Gohr            'Colon-prefixed name of the icon. Available prefixes: ' . join(', ', $prefixes), true, 'downloadSvg');
671a23d1dbSAndreas Gohr        $options->registerArgument('output', 'File to save, defaults to <name>.svg in current dir', false,
681a23d1dbSAndreas Gohr            'downloadSvg');
6992738407SAndreas Gohr        $options->registerOption('keep-ns', 'Keep the SVG namespace. Use when the file is not inlined into HTML.', 'k',
7092738407SAndreas Gohr            false, 'downloadSvg');
711a23d1dbSAndreas Gohr
728f82d673SAndreas Gohr        $options->registerCommand('cleanSvg', 'Clean a existing SVG files to reduce their file size.');
738f82d673SAndreas Gohr        $options->registerArgument('files...', 'The files to clean (will be overwritten)', true, 'cleanSvg');
7492738407SAndreas Gohr        $options->registerOption('keep-ns', 'Keep the SVG namespace. Use when the file is not inlined into HTML.', 'k',
7592738407SAndreas Gohr            false, 'cleanSvg');
765586e97bSAndreas Gohr
775586e97bSAndreas Gohr        $options->registerCommand('cleanLang',
785586e97bSAndreas Gohr            'Clean language files from unused language strings. Detecting which strings are truly in use may ' .
795586e97bSAndreas Gohr            'not always correctly work. Use with caution.');
80*ec0a687bSAndreas Gohr
81*ec0a687bSAndreas Gohr        $options->registerCommand('test', 'Run the unit tests for this extension.');
82*ec0a687bSAndreas Gohr
83*ec0a687bSAndreas Gohr        $options->registerCommand('check', 'Check for code style violations.');
84*ec0a687bSAndreas Gohr        $options->registerArgument('files...', 'The files to check. Defaults to the whole extension.', false, 'check');
85*ec0a687bSAndreas Gohr
86*ec0a687bSAndreas Gohr        $options->registerCommand('fix', 'Fix code style violations and refactor outdated code.');
87*ec0a687bSAndreas Gohr        $options->registerArgument('files...', 'The files to check. Defaults to the whole extension.', false, 'fix');
8836c0b2b4SAndreas Gohr    }
8936c0b2b4SAndreas Gohr
9036c0b2b4SAndreas Gohr    /** @inheritDoc */
9136c0b2b4SAndreas Gohr    protected function main(Options $options)
9236c0b2b4SAndreas Gohr    {
931a23d1dbSAndreas Gohr        $args = $options->getArgs();
941a23d1dbSAndreas Gohr
9536c0b2b4SAndreas Gohr        switch ($options->getCmd()) {
9636c0b2b4SAndreas Gohr            case 'init':
9736c0b2b4SAndreas Gohr                return $this->cmdInit();
9836c0b2b4SAndreas Gohr            case 'addTest':
9936c0b2b4SAndreas Gohr                $test = array_shift($args);
10036c0b2b4SAndreas Gohr                return $this->cmdAddTest($test);
10136c0b2b4SAndreas Gohr            case 'addConf':
10236c0b2b4SAndreas Gohr                return $this->cmdAddConf();
10336c0b2b4SAndreas Gohr            case 'addLang':
10436c0b2b4SAndreas Gohr                return $this->cmdAddLang();
10536c0b2b4SAndreas Gohr            case 'addComponent':
10636c0b2b4SAndreas Gohr                $type = array_shift($args);
10736c0b2b4SAndreas Gohr                $component = array_shift($args);
10836c0b2b4SAndreas Gohr                return $this->cmdAddComponent($type, $component);
10936c0b2b4SAndreas Gohr            case 'deletedFiles':
11036c0b2b4SAndreas Gohr                return $this->cmdDeletedFiles();
111c5c85a97SAndreas Gohr            case 'rmObsolete':
1121a23d1dbSAndreas Gohr                return $this->cmdRmObsolete();
1131a23d1dbSAndreas Gohr            case 'downloadSvg':
1141a23d1dbSAndreas Gohr                $ident = array_shift($args);
1151a23d1dbSAndreas Gohr                $save = array_shift($args);
11670316b84SAndreas Gohr                $keep = $options->getOpt('keep-ns');
11792738407SAndreas Gohr                return $this->cmdDownloadSVG($ident, $save, $keep);
1181a23d1dbSAndreas Gohr            case 'cleanSvg':
11970316b84SAndreas Gohr                $keep = $options->getOpt('keep-ns');
1208f82d673SAndreas Gohr                return $this->cmdCleanSVG($args, $keep);
1215586e97bSAndreas Gohr            case 'cleanLang':
1225586e97bSAndreas Gohr                return $this->cmdCleanLang();
123*ec0a687bSAndreas Gohr            case 'test':
124*ec0a687bSAndreas Gohr                return $this->cmdTest();
125*ec0a687bSAndreas Gohr            case 'check':
126*ec0a687bSAndreas Gohr                return $this->cmdCheck($args);
127*ec0a687bSAndreas Gohr            case 'fix':
128*ec0a687bSAndreas Gohr                return $this->cmdFix();
12936c0b2b4SAndreas Gohr            default:
1301a23d1dbSAndreas Gohr                $this->error('Unknown command');
13136c0b2b4SAndreas Gohr                echo $options->help();
13236c0b2b4SAndreas Gohr                return 0;
13336c0b2b4SAndreas Gohr        }
13436c0b2b4SAndreas Gohr    }
13536c0b2b4SAndreas Gohr
13636c0b2b4SAndreas Gohr    /**
13736c0b2b4SAndreas Gohr     * Get the extension name from the current working directory
13836c0b2b4SAndreas Gohr     *
13936c0b2b4SAndreas Gohr     * @throws CliException if something's wrong
14036c0b2b4SAndreas Gohr     * @param string $dir
14136c0b2b4SAndreas Gohr     * @return string[] name, type
14236c0b2b4SAndreas Gohr     */
14336c0b2b4SAndreas Gohr    protected function getTypedNameFromDir($dir)
14436c0b2b4SAndreas Gohr    {
14536c0b2b4SAndreas Gohr        $pdir = fullpath(DOKU_PLUGIN);
14636c0b2b4SAndreas Gohr        $tdir = fullpath(tpl_incdir() . '../');
14736c0b2b4SAndreas Gohr
14836c0b2b4SAndreas Gohr        if (strpos($dir, $pdir) === 0) {
14936c0b2b4SAndreas Gohr            $ldir = substr($dir, strlen($pdir));
15036c0b2b4SAndreas Gohr            $type = 'plugin';
15136c0b2b4SAndreas Gohr        } elseif (strpos($dir, $tdir) === 0) {
15236c0b2b4SAndreas Gohr            $ldir = substr($dir, strlen($tdir));
15336c0b2b4SAndreas Gohr            $type = 'template';
15436c0b2b4SAndreas Gohr        } else {
15536c0b2b4SAndreas Gohr            throw new CliException('Current directory needs to be in plugin or template directory');
15636c0b2b4SAndreas Gohr        }
15736c0b2b4SAndreas Gohr
15836c0b2b4SAndreas Gohr        $ldir = trim($ldir, '/');
15936c0b2b4SAndreas Gohr
16036c0b2b4SAndreas Gohr        if (strpos($ldir, '/') !== false) {
16136c0b2b4SAndreas Gohr            throw new CliException('Current directory has to be main extension directory');
16236c0b2b4SAndreas Gohr        }
16336c0b2b4SAndreas Gohr
16436c0b2b4SAndreas Gohr        return [$ldir, $type];
16536c0b2b4SAndreas Gohr    }
16636c0b2b4SAndreas Gohr
16736c0b2b4SAndreas Gohr    /**
16836c0b2b4SAndreas Gohr     * Interactively ask for a value from the user
16936c0b2b4SAndreas Gohr     *
17036c0b2b4SAndreas Gohr     * @param string $prompt
17136c0b2b4SAndreas Gohr     * @param bool $cache cache given value for next time?
17236c0b2b4SAndreas Gohr     * @return string
17336c0b2b4SAndreas Gohr     */
17436c0b2b4SAndreas Gohr    protected function readLine($prompt, $cache = false)
17536c0b2b4SAndreas Gohr    {
17636c0b2b4SAndreas Gohr        $value = '';
17736c0b2b4SAndreas Gohr        $default = '';
17836c0b2b4SAndreas Gohr        $cachename = getCacheName($prompt, '.readline');
17936c0b2b4SAndreas Gohr        if ($cache && file_exists($cachename)) {
18036c0b2b4SAndreas Gohr            $default = file_get_contents($cachename);
18136c0b2b4SAndreas Gohr        }
18236c0b2b4SAndreas Gohr
18336c0b2b4SAndreas Gohr        while ($value === '') {
18436c0b2b4SAndreas Gohr            echo $prompt;
18536c0b2b4SAndreas Gohr            if ($default) echo ' [' . $default . ']';
18636c0b2b4SAndreas Gohr            echo ': ';
18736c0b2b4SAndreas Gohr
18836c0b2b4SAndreas Gohr            $fh = fopen('php://stdin', 'r');
18936c0b2b4SAndreas Gohr            $value = trim(fgets($fh));
19036c0b2b4SAndreas Gohr            fclose($fh);
19136c0b2b4SAndreas Gohr
19236c0b2b4SAndreas Gohr            if ($value === '') $value = $default;
19336c0b2b4SAndreas Gohr        }
19436c0b2b4SAndreas Gohr
19536c0b2b4SAndreas Gohr        if ($cache) {
19636c0b2b4SAndreas Gohr            file_put_contents($cachename, $value);
19736c0b2b4SAndreas Gohr        }
19836c0b2b4SAndreas Gohr
19936c0b2b4SAndreas Gohr        return $value;
20036c0b2b4SAndreas Gohr    }
20136c0b2b4SAndreas Gohr
20236c0b2b4SAndreas Gohr    /**
20370316b84SAndreas Gohr     * Create the given files with their given content
20436c0b2b4SAndreas Gohr     *
20570316b84SAndreas Gohr     * Ignores all files that already exist
20670316b84SAndreas Gohr     *
20770316b84SAndreas Gohr     * @param array $files A File array as created by Skeletor::getFiles()
20836c0b2b4SAndreas Gohr     */
209fcb8165bSAndreas Gohr    protected function createFiles($files)
210fcb8165bSAndreas Gohr    {
21170316b84SAndreas Gohr        foreach ($files as $path => $content) {
21270316b84SAndreas Gohr            if (file_exists($path)) {
21370316b84SAndreas Gohr                $this->error($path . ' already exists');
21470316b84SAndreas Gohr                continue;
21536c0b2b4SAndreas Gohr            }
21636c0b2b4SAndreas Gohr
21770316b84SAndreas Gohr            io_makeFileDir($path);
21870316b84SAndreas Gohr            file_put_contents($path, $content);
21970316b84SAndreas Gohr            $this->success($path . ' created');
22036c0b2b4SAndreas Gohr        }
22136c0b2b4SAndreas Gohr    }
22236c0b2b4SAndreas Gohr
22336c0b2b4SAndreas Gohr    /**
224c5c85a97SAndreas Gohr     * Delete the given file if it exists
225c5c85a97SAndreas Gohr     *
226c5c85a97SAndreas Gohr     * @param string $file
227c5c85a97SAndreas Gohr     */
228c5c85a97SAndreas Gohr    protected function deleteFile($file)
229c5c85a97SAndreas Gohr    {
230c5c85a97SAndreas Gohr        if (!file_exists($file)) return;
231c5c85a97SAndreas Gohr        if (@unlink($file)) {
232c5c85a97SAndreas Gohr            $this->success('Delete ' . $file);
233c5c85a97SAndreas Gohr        }
234c5c85a97SAndreas Gohr    }
235c5c85a97SAndreas Gohr
236c5c85a97SAndreas Gohr    /**
237c5c85a97SAndreas Gohr     * Run git with the given arguments and return the output
238c5c85a97SAndreas Gohr     *
239c5c85a97SAndreas Gohr     * @throws CliException when the command can't be run
240c5c85a97SAndreas Gohr     * @param string ...$args
241c5c85a97SAndreas Gohr     * @return string[]
242c5c85a97SAndreas Gohr     */
243c5c85a97SAndreas Gohr    protected function git(...$args)
244c5c85a97SAndreas Gohr    {
245c5c85a97SAndreas Gohr        $args = array_map('escapeshellarg', $args);
246c5c85a97SAndreas Gohr        $cmd = 'git ' . join(' ', $args);
247c5c85a97SAndreas Gohr        $output = [];
248c5c85a97SAndreas Gohr        $result = 0;
249c5c85a97SAndreas Gohr
250c5c85a97SAndreas Gohr        $this->info($cmd);
251c5c85a97SAndreas Gohr        $last = exec($cmd, $output, $result);
252c5c85a97SAndreas Gohr        if ($last === false || $result !== 0) {
253c5c85a97SAndreas Gohr            throw new CliException('Running git failed');
254c5c85a97SAndreas Gohr        }
255c5c85a97SAndreas Gohr
256c5c85a97SAndreas Gohr        return $output;
257c5c85a97SAndreas Gohr    }
258c5c85a97SAndreas Gohr
259c5c85a97SAndreas Gohr    // region Commands
260c5c85a97SAndreas Gohr
261c5c85a97SAndreas Gohr    /**
26236c0b2b4SAndreas Gohr     * Intialize the current directory as a plugin or template
26336c0b2b4SAndreas Gohr     *
26436c0b2b4SAndreas Gohr     * @return int
26536c0b2b4SAndreas Gohr     */
26636c0b2b4SAndreas Gohr    protected function cmdInit()
26736c0b2b4SAndreas Gohr    {
26836c0b2b4SAndreas Gohr        $dir = fullpath(getcwd());
26936c0b2b4SAndreas Gohr        if ((new FilesystemIterator($dir))->valid()) {
270fcb8165bSAndreas Gohr            // existing directory, initialize from info file
271fcb8165bSAndreas Gohr            $skeletor = Skeletor::fromDir($dir);
272fcb8165bSAndreas Gohr        } else {
273fcb8165bSAndreas Gohr            // new directory, ask for info
27470316b84SAndreas Gohr            [$base, $type] = $this->getTypedNameFromDir($dir);
27536c0b2b4SAndreas Gohr            $user = $this->readLine('Your Name', true);
27636c0b2b4SAndreas Gohr            $mail = $this->readLine('Your E-Mail', true);
27736c0b2b4SAndreas Gohr            $desc = $this->readLine('Short description');
27870316b84SAndreas Gohr            $skeletor = new Skeletor($type, $base, $desc, $user, $mail);
279fcb8165bSAndreas Gohr        }
28070316b84SAndreas Gohr        $skeletor->addBasics();
28170316b84SAndreas Gohr        $this->createFiles($skeletor->getFiles());
28236c0b2b4SAndreas Gohr
283fcb8165bSAndreas Gohr        if (!is_dir("$dir/.git")) {
2848b06c9ddSAndreas Gohr            try {
2858b06c9ddSAndreas Gohr                $this->git('init');
2868b06c9ddSAndreas Gohr            } catch (CliException $e) {
2878b06c9ddSAndreas Gohr                $this->error($e->getMessage());
2888b06c9ddSAndreas Gohr            }
289fcb8165bSAndreas Gohr        }
2908b06c9ddSAndreas Gohr
29136c0b2b4SAndreas Gohr        return 0;
29236c0b2b4SAndreas Gohr    }
29336c0b2b4SAndreas Gohr
29436c0b2b4SAndreas Gohr    /**
29536c0b2b4SAndreas Gohr     * Add test framework
29636c0b2b4SAndreas Gohr     *
29736c0b2b4SAndreas Gohr     * @param string $test Name of the Test to add
29836c0b2b4SAndreas Gohr     * @return int
29936c0b2b4SAndreas Gohr     */
30036c0b2b4SAndreas Gohr    protected function cmdAddTest($test = '')
30136c0b2b4SAndreas Gohr    {
30270316b84SAndreas Gohr        $skeletor = Skeletor::fromDir(getcwd());
30370316b84SAndreas Gohr        $skeletor->addTest($test);
30470316b84SAndreas Gohr        $this->createFiles($skeletor->getFiles());
30536c0b2b4SAndreas Gohr        return 0;
30636c0b2b4SAndreas Gohr    }
30736c0b2b4SAndreas Gohr
30836c0b2b4SAndreas Gohr    /**
30936c0b2b4SAndreas Gohr     * Add configuration
31036c0b2b4SAndreas Gohr     *
31136c0b2b4SAndreas Gohr     * @return int
31236c0b2b4SAndreas Gohr     */
31336c0b2b4SAndreas Gohr    protected function cmdAddConf()
31436c0b2b4SAndreas Gohr    {
31570316b84SAndreas Gohr        $skeletor = Skeletor::fromDir(getcwd());
31670316b84SAndreas Gohr        $skeletor->addConf(is_dir('lang'));
31770316b84SAndreas Gohr        $this->createFiles($skeletor->getFiles());
31836c0b2b4SAndreas Gohr        return 0;
31936c0b2b4SAndreas Gohr    }
32036c0b2b4SAndreas Gohr
32136c0b2b4SAndreas Gohr    /**
32236c0b2b4SAndreas Gohr     * Add language
32336c0b2b4SAndreas Gohr     *
32436c0b2b4SAndreas Gohr     * @return int
32536c0b2b4SAndreas Gohr     */
32636c0b2b4SAndreas Gohr    protected function cmdAddLang()
32736c0b2b4SAndreas Gohr    {
32870316b84SAndreas Gohr        $skeletor = Skeletor::fromDir(getcwd());
32970316b84SAndreas Gohr        $skeletor->addLang(is_dir('conf'));
33070316b84SAndreas Gohr        $this->createFiles($skeletor->getFiles());
33136c0b2b4SAndreas Gohr        return 0;
33236c0b2b4SAndreas Gohr    }
33336c0b2b4SAndreas Gohr
33436c0b2b4SAndreas Gohr    /**
33536c0b2b4SAndreas Gohr     * Add another component to the plugin
33636c0b2b4SAndreas Gohr     *
33736c0b2b4SAndreas Gohr     * @param string $type
33836c0b2b4SAndreas Gohr     * @param string $component
33936c0b2b4SAndreas Gohr     */
34036c0b2b4SAndreas Gohr    protected function cmdAddComponent($type, $component = '')
34136c0b2b4SAndreas Gohr    {
34270316b84SAndreas Gohr        $skeletor = Skeletor::fromDir(getcwd());
34370316b84SAndreas Gohr        $skeletor->addComponent($type, $component);
34470316b84SAndreas Gohr        $this->createFiles($skeletor->getFiles());
34536c0b2b4SAndreas Gohr        return 0;
34636c0b2b4SAndreas Gohr    }
34736c0b2b4SAndreas Gohr
34836c0b2b4SAndreas Gohr    /**
34936c0b2b4SAndreas Gohr     * Generate a list of deleted files from git
35036c0b2b4SAndreas Gohr     *
35136c0b2b4SAndreas Gohr     * @link https://stackoverflow.com/a/6018049/172068
35236c0b2b4SAndreas Gohr     */
35336c0b2b4SAndreas Gohr    protected function cmdDeletedFiles()
35436c0b2b4SAndreas Gohr    {
3558b06c9ddSAndreas Gohr        if (!is_dir('.git')) throw new CliException('This extension seems not to be managed by git');
35636c0b2b4SAndreas Gohr
3578b06c9ddSAndreas Gohr        $output = $this->git('log', '--no-renames', '--pretty=format:', '--name-only', '--diff-filter=D');
35836c0b2b4SAndreas Gohr        $output = array_map('trim', $output);
35936c0b2b4SAndreas Gohr        $output = array_filter($output);
36036c0b2b4SAndreas Gohr        $output = array_unique($output);
36136c0b2b4SAndreas Gohr        $output = array_filter($output, function ($item) {
36236c0b2b4SAndreas Gohr            return !file_exists($item);
36336c0b2b4SAndreas Gohr        });
36436c0b2b4SAndreas Gohr        sort($output);
36536c0b2b4SAndreas Gohr
36636c0b2b4SAndreas Gohr        if (!count($output)) {
36736c0b2b4SAndreas Gohr            $this->info('No deleted files found');
36836c0b2b4SAndreas Gohr            return 0;
36936c0b2b4SAndreas Gohr        }
37036c0b2b4SAndreas Gohr
37136c0b2b4SAndreas Gohr        $content = "# This is a list of files that were present in previous releases\n" .
37236c0b2b4SAndreas Gohr            "# but were removed later. They should not exist in your installation.\n" .
37336c0b2b4SAndreas Gohr            join("\n", $output) . "\n";
37436c0b2b4SAndreas Gohr
37536c0b2b4SAndreas Gohr        file_put_contents('deleted.files', $content);
37636c0b2b4SAndreas Gohr        $this->success('written deleted.files');
37736c0b2b4SAndreas Gohr        return 0;
37836c0b2b4SAndreas Gohr    }
3798b06c9ddSAndreas Gohr
3808b06c9ddSAndreas Gohr    /**
381c5c85a97SAndreas Gohr     * Remove files that shouldn't be here anymore
3828b06c9ddSAndreas Gohr     */
3831a23d1dbSAndreas Gohr    protected function cmdRmObsolete()
3848b06c9ddSAndreas Gohr    {
385c5c85a97SAndreas Gohr        $this->deleteFile('_test/general.test.php');
386c5c85a97SAndreas Gohr        $this->deleteFile('.travis.yml');
38753bec4caSAndreas Gohr        $this->deleteFile('.github/workflows/phpTestLinux.yml');
3888b06c9ddSAndreas Gohr
389c5c85a97SAndreas Gohr        return 0;
3908b06c9ddSAndreas Gohr    }
3918b06c9ddSAndreas Gohr
3921a23d1dbSAndreas Gohr    /**
3931a23d1dbSAndreas Gohr     * Download a remote icon
3941a23d1dbSAndreas Gohr     *
3951a23d1dbSAndreas Gohr     * @param string $ident
3961a23d1dbSAndreas Gohr     * @param string $save
39792738407SAndreas Gohr     * @param bool $keep
3981a23d1dbSAndreas Gohr     * @return int
3991a23d1dbSAndreas Gohr     * @throws Exception
4001a23d1dbSAndreas Gohr     */
40192738407SAndreas Gohr    protected function cmdDownloadSVG($ident, $save = '', $keep = false)
4021a23d1dbSAndreas Gohr    {
4031a23d1dbSAndreas Gohr        $svg = new SVGIcon($this);
40492738407SAndreas Gohr        $svg->keepNamespace($keep);
4051a23d1dbSAndreas Gohr        return (int)$svg->downloadRemoteIcon($ident, $save);
4061a23d1dbSAndreas Gohr    }
4071a23d1dbSAndreas Gohr
4081a23d1dbSAndreas Gohr    /**
4098f82d673SAndreas Gohr     * @param string[] $files
41092738407SAndreas Gohr     * @param bool $keep
4111a23d1dbSAndreas Gohr     * @return int
4121a23d1dbSAndreas Gohr     * @throws Exception
4131a23d1dbSAndreas Gohr     */
4148f82d673SAndreas Gohr    protected function cmdCleanSVG($files, $keep = false)
4151a23d1dbSAndreas Gohr    {
4161a23d1dbSAndreas Gohr        $svg = new SVGIcon($this);
41792738407SAndreas Gohr        $svg->keepNamespace($keep);
4188f82d673SAndreas Gohr
4198f82d673SAndreas Gohr        $ok = true;
4208f82d673SAndreas Gohr        foreach ($files as $file) {
4218f82d673SAndreas Gohr            $ok = $ok && $svg->cleanSVGFile($file);
4228f82d673SAndreas Gohr        }
4238f82d673SAndreas Gohr        return (int)$ok;
4241a23d1dbSAndreas Gohr    }
4251a23d1dbSAndreas Gohr
4265586e97bSAndreas Gohr    /**
4275586e97bSAndreas Gohr     * @return int
4285586e97bSAndreas Gohr     */
4295586e97bSAndreas Gohr    protected function cmdCleanLang()
4305586e97bSAndreas Gohr    {
4315586e97bSAndreas Gohr        $lp = new LangProcessor($this);
4325586e97bSAndreas Gohr
4335586e97bSAndreas Gohr        $files = glob('./lang/*/lang.php');
4345b2e8f12SAndreas Gohr        foreach ($files as $file) {
4355b2e8f12SAndreas Gohr            $lp->processLangFile($file);
4365b2e8f12SAndreas Gohr        }
4375b2e8f12SAndreas Gohr
4385b2e8f12SAndreas Gohr        $files = glob('./lang/*/settings.php');
4395586e97bSAndreas Gohr        foreach ($files as $file) {
440f4f76afdSAndreas Gohr            $lp->processSettingsFile($file);
4415586e97bSAndreas Gohr        }
4425586e97bSAndreas Gohr
4435586e97bSAndreas Gohr        return 0;
4445586e97bSAndreas Gohr    }
4455586e97bSAndreas Gohr
446*ec0a687bSAndreas Gohr    /**
447*ec0a687bSAndreas Gohr     * @return int
448*ec0a687bSAndreas Gohr     */
449*ec0a687bSAndreas Gohr    protected function cmdTest()
450*ec0a687bSAndreas Gohr    {
451*ec0a687bSAndreas Gohr        $dir = fullpath(getcwd());
452*ec0a687bSAndreas Gohr        [$base, $type] = $this->getTypedNameFromDir($dir);
453*ec0a687bSAndreas Gohr
454*ec0a687bSAndreas Gohr        if ($this->colors->isEnabled()) {
455*ec0a687bSAndreas Gohr            $colors = 'always';
456*ec0a687bSAndreas Gohr        } else {
457*ec0a687bSAndreas Gohr            $colors = 'never';
458*ec0a687bSAndreas Gohr        }
459*ec0a687bSAndreas Gohr
460*ec0a687bSAndreas Gohr        $args = [
461*ec0a687bSAndreas Gohr            fullpath(__DIR__ . '/../../../_test/vendor/bin/phpunit'),
462*ec0a687bSAndreas Gohr            '--verbose',
463*ec0a687bSAndreas Gohr            "--colors=$colors",
464*ec0a687bSAndreas Gohr            '--configuration', fullpath(__DIR__ . '/../../../_test/phpunit.xml'),
465*ec0a687bSAndreas Gohr            '--group', $type . '_' . $base,
466*ec0a687bSAndreas Gohr        ];
467*ec0a687bSAndreas Gohr        $cmd = join(' ', array_map('escapeshellarg', $args));
468*ec0a687bSAndreas Gohr        $this->info("Running $cmd");
469*ec0a687bSAndreas Gohr
470*ec0a687bSAndreas Gohr        $result = 0;
471*ec0a687bSAndreas Gohr        passthru($cmd, $result);
472*ec0a687bSAndreas Gohr        return $result;
473*ec0a687bSAndreas Gohr    }
474*ec0a687bSAndreas Gohr
475*ec0a687bSAndreas Gohr    /**
476*ec0a687bSAndreas Gohr     * @return int
477*ec0a687bSAndreas Gohr     */
478*ec0a687bSAndreas Gohr    protected function cmdCheck($files = [])
479*ec0a687bSAndreas Gohr    {
480*ec0a687bSAndreas Gohr        $dir = fullpath(getcwd());
481*ec0a687bSAndreas Gohr
482*ec0a687bSAndreas Gohr        $args = [
483*ec0a687bSAndreas Gohr            fullpath(__DIR__ . '/../../../_test/vendor/bin/phpcs'),
484*ec0a687bSAndreas Gohr            '--standard=' . fullpath(__DIR__ . '/../../../_test/phpcs.xml'),
485*ec0a687bSAndreas Gohr            ($this->colors->isEnabled()) ? '--colors' : '--no-colors',
486*ec0a687bSAndreas Gohr            '--',
487*ec0a687bSAndreas Gohr        ];
488*ec0a687bSAndreas Gohr
489*ec0a687bSAndreas Gohr        if ($files) {
490*ec0a687bSAndreas Gohr            $args = array_merge($args, $files);
491*ec0a687bSAndreas Gohr        } else {
492*ec0a687bSAndreas Gohr            $args[] = fullpath($dir);
493*ec0a687bSAndreas Gohr        }
494*ec0a687bSAndreas Gohr
495*ec0a687bSAndreas Gohr        $cmd = join(' ', array_map('escapeshellarg', $args));
496*ec0a687bSAndreas Gohr        $this->info("Running $cmd");
497*ec0a687bSAndreas Gohr
498*ec0a687bSAndreas Gohr        $result = 0;
499*ec0a687bSAndreas Gohr        passthru($cmd, $result);
500*ec0a687bSAndreas Gohr        return $result;
501*ec0a687bSAndreas Gohr    }
502*ec0a687bSAndreas Gohr
503*ec0a687bSAndreas Gohr    /**
504*ec0a687bSAndreas Gohr     * @return int
505*ec0a687bSAndreas Gohr     */
506*ec0a687bSAndreas Gohr    protected function cmdFix($files = [])
507*ec0a687bSAndreas Gohr    {
508*ec0a687bSAndreas Gohr        $dir = fullpath(getcwd());
509*ec0a687bSAndreas Gohr
510*ec0a687bSAndreas Gohr        // first run rector to refactor outdated code
511*ec0a687bSAndreas Gohr        $args = [
512*ec0a687bSAndreas Gohr            fullpath(__DIR__ . '/../../../_test/vendor/bin/rector'),
513*ec0a687bSAndreas Gohr            ($this->colors->isEnabled()) ? '--ansi' : '--no-ansi',
514*ec0a687bSAndreas Gohr            '--config=' . fullpath(__DIR__ . '/../../../_test/rector.php'),
515*ec0a687bSAndreas Gohr            '--no-diffs',
516*ec0a687bSAndreas Gohr            'process',
517*ec0a687bSAndreas Gohr        ];
518*ec0a687bSAndreas Gohr
519*ec0a687bSAndreas Gohr        if ($files) {
520*ec0a687bSAndreas Gohr            $args = array_merge($args, $files);
521*ec0a687bSAndreas Gohr        } else {
522*ec0a687bSAndreas Gohr            $args[] = fullpath($dir);
523*ec0a687bSAndreas Gohr        }
524*ec0a687bSAndreas Gohr
525*ec0a687bSAndreas Gohr        $cmd = join(' ', array_map('escapeshellarg', $args));
526*ec0a687bSAndreas Gohr        $this->info("Running $cmd");
527*ec0a687bSAndreas Gohr
528*ec0a687bSAndreas Gohr        $result = 0;
529*ec0a687bSAndreas Gohr        passthru($cmd, $result);
530*ec0a687bSAndreas Gohr        if($result !== 0) return $result;
531*ec0a687bSAndreas Gohr
532*ec0a687bSAndreas Gohr        // now run phpcbf to clean up code style
533*ec0a687bSAndreas Gohr        $args = [
534*ec0a687bSAndreas Gohr            fullpath(__DIR__ . '/../../../_test/vendor/bin/phpcbf'),
535*ec0a687bSAndreas Gohr            '--standard=' . fullpath(__DIR__ . '/../../../_test/phpcs.xml'),
536*ec0a687bSAndreas Gohr            ($this->colors->isEnabled()) ? '--colors' : '--no-colors',
537*ec0a687bSAndreas Gohr            '--',
538*ec0a687bSAndreas Gohr        ];
539*ec0a687bSAndreas Gohr
540*ec0a687bSAndreas Gohr        if ($files) {
541*ec0a687bSAndreas Gohr            $args = array_merge($args, $files);
542*ec0a687bSAndreas Gohr        } else {
543*ec0a687bSAndreas Gohr            $args[] = fullpath($dir);
544*ec0a687bSAndreas Gohr        }
545*ec0a687bSAndreas Gohr
546*ec0a687bSAndreas Gohr        $cmd = join(' ', array_map('escapeshellarg', $args));
547*ec0a687bSAndreas Gohr        $this->info("Running $cmd");
548*ec0a687bSAndreas Gohr
549*ec0a687bSAndreas Gohr        $result = 0;
550*ec0a687bSAndreas Gohr        passthru($cmd, $result);
551*ec0a687bSAndreas Gohr        return $result;
552*ec0a687bSAndreas Gohr    }
553*ec0a687bSAndreas Gohr
554c5c85a97SAndreas Gohr    //endregion
55536c0b2b4SAndreas Gohr}
556