Multiple fixes
* Implemented ScriptRunner with environment expansion and cleaner code. * Added ApiClient plugin (com.noccy.apiclient) * Renamed CHANGELOG.md to VERSIONS.md * Shuffled buildtools * Added first unittests
This commit is contained in:
		@@ -70,10 +70,10 @@ class PluginsCommand extends Command
 | 
			
		||||
                $info = object();
 | 
			
		||||
            }
 | 
			
		||||
            $installed = in_array(basename($plugin), $localPluginList);
 | 
			
		||||
            $badge = ($installed)?"<fg=green>\u{2714}</>":"<fg=gray>\u{27f3}</>";
 | 
			
		||||
            $output->writeln(sprintf(" %s <fg=#0ff>%-20s</>  %s", $badge, basename($plugin), $info->name??null));
 | 
			
		||||
            $badge = ($installed)?"<fg=green>\u{2714}</>":"<fg=gray>\u{25cc}</>";
 | 
			
		||||
            $output->writeln(sprintf(" %s <fg=%s>%-20s</>  %s", $badge, ($installed?"#0ff":"#088"), basename($plugin), $info->name??null));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return Command::SUCCESS;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -34,7 +34,9 @@ class RunCommand extends Command
 | 
			
		||||
            }
 | 
			
		||||
            return Command::SUCCESS;
 | 
			
		||||
        } elseif ($script = $input->getArgument('script')) {
 | 
			
		||||
            $env->runScript($script, $args, $input, $output);
 | 
			
		||||
            $runner = $env->getScriptRunner();
 | 
			
		||||
            //$env->runScript($script, $args, $input, $output);
 | 
			
		||||
            $runner->evaluateDefinedScript($script); // args?
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return Command::SUCCESS;
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ use Spark\SparkApplication;
 | 
			
		||||
use Symfony\Component\Console\Input\InputInterface;
 | 
			
		||||
use Symfony\Component\Console\Output\OutputInterface;
 | 
			
		||||
use Symfony\Component\Console\Style\SymfonyStyle;
 | 
			
		||||
use Symfony\Component\Dotenv\Dotenv;
 | 
			
		||||
 | 
			
		||||
class Environment
 | 
			
		||||
{
 | 
			
		||||
@@ -36,12 +37,23 @@ class Environment
 | 
			
		||||
        return array_keys($this->config['scripts']??[]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getScriptRunner(): ScriptRunner
 | 
			
		||||
    {
 | 
			
		||||
        $runner = new ScriptRunner();
 | 
			
		||||
        $runner->setDirectory($this->getProjectDirectory());
 | 
			
		||||
        foreach ((array)$this->config['scripts'] as $name => $script) {
 | 
			
		||||
            $runner->defineScript($name, $script);
 | 
			
		||||
        }
 | 
			
		||||
        return $runner;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    public function runScript(string $name, array $args, InputInterface $input, OutputInterface $output)
 | 
			
		||||
    {
 | 
			
		||||
        $script = $this->config['scripts'][$name]??$name;
 | 
			
		||||
        
 | 
			
		||||
        if (is_string($script)) {
 | 
			
		||||
            $this->execScript($script, $args);
 | 
			
		||||
            $this->execScript($script, $args, $output);
 | 
			
		||||
        } elseif (is_array($script)) {
 | 
			
		||||
            foreach ($script as $row) {
 | 
			
		||||
                $a = str_getcsv($row, ' ', "'");
 | 
			
		||||
@@ -50,13 +62,13 @@ class Environment
 | 
			
		||||
                    $c = ($this->config['scripts'][substr($c,1)])??$c;
 | 
			
		||||
                    $this->runScript($c, $a, $input, $output);
 | 
			
		||||
                } else {
 | 
			
		||||
                    $this->execScript($c, $a);
 | 
			
		||||
                    $this->execScript($c, $a, $output);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function execScript(string $script, array $args)
 | 
			
		||||
    private function execScript(string $script, array $args, OutputInterface $output)
 | 
			
		||||
    {
 | 
			
		||||
            // call script directly
 | 
			
		||||
            if (str_ends_with($script, '.php')) {
 | 
			
		||||
@@ -74,6 +86,7 @@ class Environment
 | 
			
		||||
                passthru($script);
 | 
			
		||||
            }
 | 
			
		||||
    }
 | 
			
		||||
    */
 | 
			
		||||
 | 
			
		||||
    public function loadEnvironment()
 | 
			
		||||
    {
 | 
			
		||||
@@ -84,9 +97,24 @@ class Environment
 | 
			
		||||
        if (!array_key_exists('project_dir', $this->config)) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        chdir($this->config['project_dir']);
 | 
			
		||||
 | 
			
		||||
        $envfile = $this->config['project_dir']."/.env";
 | 
			
		||||
        if (file_exists($envfile)) {
 | 
			
		||||
            $dotenv = new Dotenv();
 | 
			
		||||
            $dotenv->load($envfile);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $blacklistFile = $this->config['config_dir']."/blacklist";
 | 
			
		||||
        if (file_exists($blacklistFile)) {
 | 
			
		||||
            $blacklist = json_decode(file_get_contents($blacklistFile), true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (empty($this->config['preload'])) {
 | 
			
		||||
            fprintf(STDERR, "Error: Missing or malformed spark.json file.\n");
 | 
			
		||||
            exit(1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // $this->logger->info("Loading environment...");
 | 
			
		||||
        $preloads = [];
 | 
			
		||||
        $root = $this->config['project_dir'];
 | 
			
		||||
@@ -102,7 +130,7 @@ class Environment
 | 
			
		||||
            if (!str_starts_with($item, "/")) {
 | 
			
		||||
                $item = $this->getProjectDirectory() . "/" . $item;
 | 
			
		||||
            }
 | 
			
		||||
            if (is_file($item)) {
 | 
			
		||||
            if (is_file($item) && fnmatch("*.php", $item)) {
 | 
			
		||||
                // $this->logger->debug("Preloading file {$item}");
 | 
			
		||||
                try {
 | 
			
		||||
                    include_once($item);
 | 
			
		||||
@@ -119,8 +147,8 @@ class Environment
 | 
			
		||||
                    //$this->logger->error("Error preloading plugin {$item}: {$t->getMessage()} in {$t->getFile()} on line {$t->getLine()}");
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                fprintf(STDERR, "warning: Could not preload %s\n", $item);
 | 
			
		||||
                //$this->logger->warning("Could not preload {$item}");
 | 
			
		||||
                // fprintf(STDERR, "warning: Could not preload %s\n", $item);
 | 
			
		||||
                // $this->logger->warning("Could not preload {$item}");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -163,6 +191,11 @@ class Environment
 | 
			
		||||
        return $env;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public function expandString(string $input): string
 | 
			
		||||
    {
 | 
			
		||||
        return preg_replace_callback('/([\%\$]\{(.+?)\}/', function ($v) {
 | 
			
		||||
            print_r($v);
 | 
			
		||||
        }, $input);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										91
									
								
								src/Environment/ScriptRunner.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								src/Environment/ScriptRunner.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,91 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace Spark\Environment;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ScriptRunner
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    private array $scripts = [];
 | 
			
		||||
 | 
			
		||||
    private ?string $directory = null;
 | 
			
		||||
 | 
			
		||||
    public function setDirectory(?string $directory)
 | 
			
		||||
    {
 | 
			
		||||
        $this->directory = $directory;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function defineScript(string $name, string|array $script)
 | 
			
		||||
    {
 | 
			
		||||
        $this->scripts[$name] = $script;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function evaluateDefinedScript(string $name)
 | 
			
		||||
    {
 | 
			
		||||
        $script = $this->scripts[$name];
 | 
			
		||||
        $this->evaluate($script);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function evaluate(string|array $script)
 | 
			
		||||
    {
 | 
			
		||||
        if (is_array($script)) {
 | 
			
		||||
            foreach ($script as $step) {
 | 
			
		||||
                $this->evaluate($step);
 | 
			
		||||
            }
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $script = $this->expandString($script);
 | 
			
		||||
 | 
			
		||||
        // Determine what to do
 | 
			
		||||
        if (str_starts_with($script, '@')) {
 | 
			
		||||
            // starts with @, call on a defined script
 | 
			
		||||
            $subname = substr($script, 1);
 | 
			
		||||
            $subscript = $this->scripts[$subname];
 | 
			
		||||
            $this->evaluate($subscript);
 | 
			
		||||
        } else {
 | 
			
		||||
            if (posix_isatty(STDOUT)) {
 | 
			
		||||
                printf("\e[0;33m> \e[0;93m%s\e[0m\n", $script);
 | 
			
		||||
            } else {
 | 
			
		||||
                printf("> %s\n", $script);
 | 
			
		||||
            }
 | 
			
		||||
    
 | 
			
		||||
            if (str_contains($script, ' ')) {
 | 
			
		||||
                [$script, $args] = explode(" ", $script, 2);
 | 
			
		||||
                $args = str_getcsv($args, ' ', "'");
 | 
			
		||||
                
 | 
			
		||||
            } else {
 | 
			
		||||
                $args = [];
 | 
			
		||||
            }
 | 
			
		||||
            if (is_callable($script)) {
 | 
			
		||||
                // call script
 | 
			
		||||
                call_user_func($script, ...$args);
 | 
			
		||||
            } elseif (file_exists((string)$script) && fnmatch("*.php", (string)$script)) {
 | 
			
		||||
                include $script;
 | 
			
		||||
            } else {
 | 
			
		||||
                // call shell
 | 
			
		||||
                $cmdl = trim(escapeshellcmd((string)$script) . " " . join(" ", array_map("escapeshellarg", $args)));
 | 
			
		||||
                $proc = proc_open($cmdl, [ 0 => STDIN, 1 => STDOUT, 2 => STDERR ], $pipes, $this->directory);
 | 
			
		||||
                while ($stat = proc_get_status($proc)) {
 | 
			
		||||
                    if ($stat['running'] === false) {
 | 
			
		||||
                        $ec = $stat['exitcode'];
 | 
			
		||||
                        if ($ec != 0) {
 | 
			
		||||
                            printf("\e[31mcommand exited with code %d.\e[0m\n", $stat['exitcode']);
 | 
			
		||||
                            throw new \RuntimeException("Command {$cmdl} exited with code {$ec}");
 | 
			
		||||
                        }
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                    usleep(100000);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function expandString(string $input)
 | 
			
		||||
    {
 | 
			
		||||
        return preg_replace_callback('/(\$\{(.+?)\})/', function ($match) {
 | 
			
		||||
 | 
			
		||||
            return ($_ENV[$match[2]]??getenv($match[2]))??null;
 | 
			
		||||
        }, $input);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -25,7 +25,7 @@ class SparkApplication extends Application
 | 
			
		||||
 | 
			
		||||
    public function __construct()
 | 
			
		||||
    {
 | 
			
		||||
        parent::__construct("Spark", APP_VERSION);
 | 
			
		||||
        parent::__construct("Spark\u{26a1}", APP_VERSION);
 | 
			
		||||
        self::$instance = $this;
 | 
			
		||||
 | 
			
		||||
        $this->resourceManager = new ResourceManager();
 | 
			
		||||
@@ -39,6 +39,10 @@ class SparkApplication extends Application
 | 
			
		||||
        $this->add(new Commands\ReplCommand());
 | 
			
		||||
        $this->add(new Commands\InitCommand());
 | 
			
		||||
 | 
			
		||||
        $this->get("list")->setHidden(true);
 | 
			
		||||
        $this->get("completion")->setHidden(true);
 | 
			
		||||
        $this->get("help")->setHidden(true);
 | 
			
		||||
 | 
			
		||||
        if (getenv("SPARK_PLUGINS")) {
 | 
			
		||||
            $this->add(new Commands\PluginsCommand());
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										30
									
								
								src/install
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								src/install
									
									
									
									
									
								
							@@ -1,5 +1,10 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
if (!(file_exists(getcwd()."/plugins") && file_exists(getcwd()."/spark.phar"))) {
 | 
			
		||||
    fwrite(STDERR, "Not running from installer directory! Already installed?\n");
 | 
			
		||||
    exit(1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function askConfirm(string $prompt, bool $default) {
 | 
			
		||||
 | 
			
		||||
    $pstr = sprintf("%s [%s]? ", $prompt, $default?"Y/n":"y/N");
 | 
			
		||||
@@ -26,7 +31,13 @@ function askString(string $prompt, ?string $default=null) {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
printf("\n%s\n\e[1mSpark\e[0m Installer\n%s\n\n", str_repeat("\u{2500}",40), str_repeat("\u{2500}", 40));
 | 
			
		||||
echo " ___                _   \n";
 | 
			
		||||
echo "/ __|_ __  __ _ _ _| |__\n";
 | 
			
		||||
echo "\\__ \\ '_ \\/ _` | '_| / /\n";
 | 
			
		||||
echo "|___/ .__/\\__,_|_| |_\\_\\\n";
 | 
			
		||||
echo "    |_|                 \n";
 | 
			
		||||
 | 
			
		||||
printf("\n%s\n \u{26a1} \e[1mSpark\e[0m Installer\n%s\n\n", str_repeat("\u{2500}",40), str_repeat("\u{2500}", 40));
 | 
			
		||||
 | 
			
		||||
$destination = askString("Installation directory", getenv("HOME")."/opt/spark");
 | 
			
		||||
$binaries = askString("Path for executables", getenv("HOME")."/bin");
 | 
			
		||||
@@ -57,14 +68,17 @@ passthru("cp -R plugins/* ".escapeshellarg($destination."/plugins/"));
 | 
			
		||||
 | 
			
		||||
if ($doPlugins) {
 | 
			
		||||
    $file = sprintf("export SPARK_PLUGINS=\"%s/plugins\"\n", $destination);
 | 
			
		||||
    file_put_contents(getenv("HOME")."/.profile_spark", $file);
 | 
			
		||||
    file_put_contents(getenv("HOME")."/.bashrc_spark", $file);
 | 
			
		||||
    printf("Updated \e[3m.bashrc_spark\e[0m.\n");
 | 
			
		||||
    
 | 
			
		||||
    $file = file_get_contents(getenv("HOME")."/.profile");
 | 
			
		||||
    $file .= "\nsource ~/.profile_spark\n";
 | 
			
		||||
    file_put_contents(getenv("HOME")."/.profile.new", $file);
 | 
			
		||||
    rename(getenv("HOME")."/.profile", getenv("HOME")."/.profile.bak");
 | 
			
		||||
    rename(getenv("HOME")."/.profile.new", getenv("HOME")."/.profile");
 | 
			
		||||
    printf("Updated \e[3m.profile\e[0m.\n");
 | 
			
		||||
    $file = file_get_contents(getenv("HOME")."/.bashrc");
 | 
			
		||||
    if (!str_contains($file, ".bashrc_spark")) {
 | 
			
		||||
        $file .= "\nsource ~/.bashrc_spark\n";
 | 
			
		||||
        file_put_contents(getenv("HOME")."/.bashrc.new", $file);
 | 
			
		||||
        rename(getenv("HOME")."/.bashrc", getenv("HOME")."/.bashrc.bak");
 | 
			
		||||
        rename(getenv("HOME")."/.bashrc.new", getenv("HOME")."/.bashrc");
 | 
			
		||||
        printf("Updated \e[3m.bashrc\e[0m.\n");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
if ($doAliases) {
 | 
			
		||||
    $file = file_get_contents(getenv("HOME")."/.bash_aliases") . "\n";
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user