Plugin manager, misc fixes

* Fixed an error when trying to create nonexisting resource type
* Updated the init-command to create plugins and scipts directories
* Added a basic installer function
This commit is contained in:
Chris 2021-12-09 00:12:37 +01:00
parent 0d8844f499
commit d940a94611
7 changed files with 181 additions and 2 deletions

8
CHANGELOG.md Normal file
View File

@ -0,0 +1,8 @@
# Changelog
## 0.1.0
- Initial beta release version.
- Plugin manager: If `SPARK_PLUGINS` envvar is set, the `plugins` command will be
available to manage symlinks to the globally installed plugins.

View File

@ -9,5 +9,10 @@ else {
require_once __DIR__."/../vendor/autoload.php"; require_once __DIR__."/../vendor/autoload.php";
if ($argc == 2 && $argv[1] === "install") {
require_once __DIR__."/../src/install";
exit(0);
}
$app = new Spark\SparkApplication(); $app = new Spark\SparkApplication();
$app->run(); $app->run();

View File

@ -26,12 +26,14 @@ class InitCommand extends Command
mkdir($configDir); mkdir($configDir);
mkdir($configDir."/plugins"); mkdir($configDir."/plugins");
mkdir($configDir."/scripts");
file_put_contents($configDir."/spark.json", json_encode([ file_put_contents($configDir."/spark.json", json_encode([
'preload' => [ 'preload' => [
'./.spark/plugins/*' './.spark/plugins/*',
'./.spark/scripts/*'
], ],
'scripts' => [ 'scripts' => [
'hello' => "echo 'Hello World\n'" 'hello' => "echo 'Hello World'"
] ]
], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES)); ], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES));

View File

@ -0,0 +1,79 @@
<?php
namespace Spark\Commands;
use Spark\SparkApplication;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
#[AsCommand(name:'plugins', description:'Manage plugins from global repository')]
class PluginsCommand extends Command
{
protected function configure()
{
$this->addOption("enable", null, InputOption::VALUE_REQUIRED, "Enable (symlink) a plugin");
$this->addOption("disable", null, InputOption::VALUE_REQUIRED, "Disable (remove) a plugin");
$this->addOption("force", "f", InputOption::VALUE_NONE, "Remove a plugin even if not a symlink");
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$env = $this->getEnvironment();
$app = $this->getApplication();
$globalPlugins = getenv("SPARK_PLUGINS");
$localPlugins = $env->getConfigDirectory()."/plugins";
$globalPluginList = glob($globalPlugins."/*");
$globalBasePluginList = array_map("basename", $globalPluginList);
$localPluginList = array_map("basename", glob($localPlugins."/*"));
if ($plugin = $input->getOption("enable")) {
if (in_array($plugin, $localPluginList)) {
$output->writeln("Plugin <info>{$plugin}</> already enabled!");
return Command::SUCCESS;
}
if (!in_array($plugin, $globalBasePluginList)) {
$output->writeln("<error>No such plugin {$plugin}</>");
return Command::FAILURE;
}
symlink($globalPlugins."/".$plugin, $localPlugins."/".$plugin);
$output->writeln("Enabled plugin <info>{$plugin}</>!");
return Command::SUCCESS;
} elseif ($plugin = $input->getOption("disable")) {
$force = $input->getOption("force");
if (!in_array($plugin, $localPluginList)) {
$output->writeln("<error>Plugin {$plugin} not enabled!</>");
return Command::FAILURE;
}
if (!is_link($localPlugins."/".$plugin) && !$force) {
$output->writeln("<error>Plugin is not a symlink and --force not specified</>");
return Command::FAILURE;
} elseif (is_link($localPlugins."/".$plugin)) {
unlink($localPlugins."/".$plugin);
$output->writeln("Disabled plugin <info>{$plugin}</>!");
return Command::SUCCESS;
}
}
foreach ($globalPluginList as $plugin) {
if (!is_dir($plugin)) continue;
if (!file_exists($plugin."/sparkplug.php")) continue;
$head = (file($plugin."/sparkplug.php", FILE_IGNORE_NEW_LINES)[0])??null;
if (str_contains($head, "//")) {
$json = "{" . substr($head, strpos($head, "//") + 3) . "}";
$info = json_decode($json)??object();
} else {
$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));
}
return Command::SUCCESS;
}
}

View File

@ -18,6 +18,9 @@ class ResourceManager
public function createResource(string $type, array $options) public function createResource(string $type, array $options)
{ {
if (!array_key_exists($type, $this->resourceTypes)) {
return null;
}
$resource = new $this->resourceTypes[$type]($options); $resource = new $this->resourceTypes[$type]($options);
return $resource; return $resource;
} }

View File

@ -39,6 +39,10 @@ class SparkApplication extends Application
$this->add(new Commands\ReplCommand()); $this->add(new Commands\ReplCommand());
$this->add(new Commands\InitCommand()); $this->add(new Commands\InitCommand());
if (getenv("SPARK_PLUGINS")) {
$this->add(new Commands\PluginsCommand());
}
} }
public function getPluginManager(): PluginManager public function getPluginManager(): PluginManager

78
src/install Normal file
View File

@ -0,0 +1,78 @@
<?php
function askConfirm(string $prompt, bool $default) {
$pstr = sprintf("%s [%s]? ", $prompt, $default?"Y/n":"y/N");
$read = readline($pstr);
switch (strtolower($read)) {
case 'yes':
case 'y':
return true;
case 'no':
case 'n':
return false;
default:
return $default;
}
}
function askString(string $prompt, ?string $default=null) {
$pstr = sprintf("%s [%s]? ", $prompt, $default);
$read = readline($pstr);
if (empty($read)) return $default;
return $read;
}
$destination = askString("Installation directory", getenv("HOME")."/opt/spark");
$binaries = askString("Path for executables", getenv("HOME")."/bin");
if (!is_dir($destination)) {
$doMakeDir = askConfirm("Create directory", true);
} else {
$doMakeDir = false;
}
$doAliases = askConfirm("Install default aliases", true);
$doPlugins = askConfirm("Install plugins globally", true);
if (!askConfirm("Continue with the installation", false)) {
exit(0);
}
if ($doMakeDir) {
printf("Creating directories...\n");
mkdir($destination, 0777, true);
mkdir($destination."/plugins", 0777, true);
}
printf("Installing Spark...\n");
passthru("cp -R spark.phar ".escapeshellarg($destination."/spark.phar"));
@symlink($destination."/spark.phar", $binaries."/spark");
printf("Installing plugins...\n");
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 = 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");
}
if ($doAliases) {
$file = file_get_contents(getenv("HOME")."/.bash_aliases") . "\n";
$file .= "alias sparksh=\"spark repl\"\n";
$file .= "alias sparker=\"spark run\"\n";
$file .= "alias sparkplug=\"spark plugins\"\n";
file_put_contents(getenv("HOME")."/.bash_aliases.new", $file);
rename(getenv("HOME")."/.bash_aliases", getenv("HOME")."/.bash_aliases.bak");
rename(getenv("HOME")."/.bash_aliases.new", getenv("HOME")."/.bash_aliases");
printf("Updated \e[3m.bash_aliases\e[0m.\n");
}
printf("Installation complete!\nPlease restart your shell, or \e[3msource ~/.bash_aliases\e[0m and try typing \e[3mspark\e[0m.\n");