From 8cc1eac7a4dc1a550b36f6f907347d476325d6a6 Mon Sep 17 00:00:00 2001 From: Christopher Vagnetoft Date: Sat, 11 Dec 2021 01:44:01 +0100 Subject: [PATCH] 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 --- .spark/build/package.sh | 44 + .spark/{ => build}/update-version.sh | 2 +- .spark/package.sh | 33 - .spark/spark.json | 13 +- README.md | 14 +- CHANGELOG.md => VERSIONS.md | 5 +- composer.json | 5 +- phpunit.xml | 26 + plugins/com.noccy.apiclient/Api/Catalog.php | 84 ++ plugins/com.noccy.apiclient/Api/Method.php | 36 + plugins/com.noccy.apiclient/Api/Profile.php | 13 + .../Commands/ApiCatalogCommand.php | 91 ++ .../Commands/ApiLogsCommand.php | 35 + .../Commands/ApiProfileCommand.php | 22 + .../Commands/ApiRequestCommand.php | 134 +++ plugins/com.noccy.apiclient/README.md | 73 ++ .../Request/HttpRequest.php | 91 ++ .../Request/JsonRpcRequest.php | 26 + .../com.noccy.apiclient/Request/Request.php | 16 + .../Request/RequestBuilder.php | 100 +++ .../Request/WebsocketRequest.php | 26 + plugins/com.noccy.apiclient/sparkplug.php | 109 +++ .../DockerDbExportCommand.php | 8 +- src/Commands/PluginsCommand.php | 6 +- src/Commands/RunCommand.php | 4 +- src/Environment/Environment.php | 49 +- src/Environment/ScriptRunner.php | 91 ++ src/SparkApplication.php | 6 +- src/install | 30 +- tests/Environment/ScriptRunnerTest.php | 30 + tools/makeself | 823 +++++++++++++++++- tools/makeself-2.4.5/makeself.sh | 822 ----------------- tools/{makeself-2.4.5 => }/makeself-header.sh | 0 33 files changed, 1976 insertions(+), 891 deletions(-) create mode 100755 .spark/build/package.sh rename .spark/{ => build}/update-version.sh (83%) delete mode 100755 .spark/package.sh rename CHANGELOG.md => VERSIONS.md (64%) create mode 100644 phpunit.xml create mode 100644 plugins/com.noccy.apiclient/Api/Catalog.php create mode 100644 plugins/com.noccy.apiclient/Api/Method.php create mode 100644 plugins/com.noccy.apiclient/Api/Profile.php create mode 100644 plugins/com.noccy.apiclient/Commands/ApiCatalogCommand.php create mode 100644 plugins/com.noccy.apiclient/Commands/ApiLogsCommand.php create mode 100644 plugins/com.noccy.apiclient/Commands/ApiProfileCommand.php create mode 100644 plugins/com.noccy.apiclient/Commands/ApiRequestCommand.php create mode 100644 plugins/com.noccy.apiclient/README.md create mode 100644 plugins/com.noccy.apiclient/Request/HttpRequest.php create mode 100644 plugins/com.noccy.apiclient/Request/JsonRpcRequest.php create mode 100644 plugins/com.noccy.apiclient/Request/Request.php create mode 100644 plugins/com.noccy.apiclient/Request/RequestBuilder.php create mode 100644 plugins/com.noccy.apiclient/Request/WebsocketRequest.php create mode 100644 plugins/com.noccy.apiclient/sparkplug.php create mode 100644 src/Environment/ScriptRunner.php create mode 100644 tests/Environment/ScriptRunnerTest.php mode change 120000 => 100755 tools/makeself delete mode 100755 tools/makeself-2.4.5/makeself.sh rename tools/{makeself-2.4.5 => }/makeself-header.sh (100%) diff --git a/.spark/build/package.sh b/.spark/build/package.sh new file mode 100755 index 0000000..92f8df8 --- /dev/null +++ b/.spark/build/package.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +VERSION="$(git describe --tags)" +PATH="$PWD/tools:$PATH" + +if [ -z "$VERSION" ]; then + echo "Could not parse version from git" + exit 1 +fi + +if [ -z $DESTINATION ]; then + DESTINATION="release/$VERSION" +fi +echo " DESTINATION=$DESTINATION" + +echo "* Preparing release direcory" +mkdir -p $DESTINATION + +echo "* Building phar archive" +pharlite &>/dev/null + +echo "* Copying files to release directory" +cp spark.phar $DESTINATION/spark.phar +cp README.md $DESTINATION/README.md +cp VERSIONS.md $DESTINATION/VERSIONS.md + +echo "* Building dist and source archives" +7z a -tzip "$DESTINATION/spark-$VERSION-dist.zip" spark.phar plugins README.md VERSIONS.md >/dev/null +tar cfz "$DESTINATION/spark-$VERSION-dist.tgz" spark.phar plugins README.md VERSIONS.md +7z a -tzip "$DESTINATION/spark-$VERSION-src.zip" bin src runtime plugins composer.json README.md VERSIONS.md >/dev/null +tar cfz "$DESTINATION/spark-$VERSION-src.tgz" bin src runtime plugins composer.json README.md VERSIONS.md + +echo "* Creating makeself installer" +test -d release/tmp && rm -rf release/tmp +mkdir release/tmp +cp -R spark.phar plugins README.md VERSIONS.md release/tmp/ +pushd release/tmp &>/dev/null +makeself . ../../$DESTINATION/spark-$VERSION-dist.run "Spark $VERSION" ./spark.phar install &>/dev/null +popd &>/dev/null +rm -rf release/tmp + +echo -e " ┌─[$DESTINATION]" +ls -hl --color=auto $DESTINATION | sed "s/^/ │ /" + diff --git a/.spark/update-version.sh b/.spark/build/update-version.sh similarity index 83% rename from .spark/update-version.sh rename to .spark/build/update-version.sh index 7f22c01..9a3dd7b 100755 --- a/.spark/update-version.sh +++ b/.spark/build/update-version.sh @@ -8,5 +8,5 @@ if [ -z "$VERSION" ]; then fi echo -e " src/version -echo "[Version: $VERSION]" +echo -e "* Version: \e[1m$VERSION\e[0m" diff --git a/.spark/package.sh b/.spark/package.sh deleted file mode 100755 index 6beb751..0000000 --- a/.spark/package.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash - -VERSION="$(git describe --tags)" -PATH="$PWD/tools:$PATH" - -if [ -z "$VERSION" ]; then - echo "Could not parse version from git" - exit 1 -fi - -mkdir -p release/$VERSION - -pharlite - -cp spark.phar release/$VERSION/spark.phar -cp README.md release/$VERSION/README.md -cp CHANGELOG.md release/$VERSION/CHANGELOG.md -7z a -tzip "release/$VERSION/spark-$VERSION-dist.zip" spark.phar plugins README.md CHANGELOG.md -tar cvfz "release/$VERSION/spark-$VERSION-dist.tgz" spark.phar plugins README.md CHANGELOG.md -7z a -tzip "release/$VERSION/spark-$VERSION-src.zip" bin src runtime plugins composer.json README.md CHANGELOG.md -tar cvfz "release/$VERSION/spark-$VERSION-src.tgz" bin src runtime plugins composer.json README.md CHANGELOG.md - -test -d release/tmp && rm -rf release/tmp -mkdir release/tmp -cp -R spark.phar plugins README.md CHANGELOG.md release/tmp/ -pushd release/tmp -makeself . ../$VERSION/spark-$VERSION-dist.run "Spark $VERSION" ./spark.phar install -popd -rm -rf release/tmp - -echo -e "\e[1m[release/$VERSION]\e[0m" -ls -hl release/$VERSION - diff --git a/.spark/spark.json b/.spark/spark.json index 7de7af6..f3f4ea7 100644 --- a/.spark/spark.json +++ b/.spark/spark.json @@ -1,11 +1,18 @@ { "preload": [ - ".spark/plugins/*" + ".spark/plugins/*", + ".spark/local/*" ], "scripts": { + "version": [ + ".spark/build/update-version.sh" + ], + "package": [ + ".spark/build/package.sh" + ], "build": [ - ".spark/update-version.sh", - ".spark/package.sh" + "@version", + "@package" ] } } diff --git a/README.md b/README.md index c853986..c92cb38 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ The advantage of writing your extensions as plugins: Using scripts is the simplest way to leverage Spark: *spark.json* -``` +```json { ... "scripts": { @@ -95,14 +95,16 @@ Using scripts is the simplest way to leverage Spark: } ``` -`.php`-files are executed in-process, and as such have access to any registered -resources, resource types and plugins. - -*Note: The script system need to be improved and revamped to support environment variables and such* +- Call on other scripts by prepending `@` to the script name. +- `.php`-files are executed in-process, and as such have access to any registered + resources, resource types and plugins. +- `.phar` files are still executed out-of-process, as are any commands that don't + match a PHP callable or any other specific rule. +- Substitute shell variables using `${varname}`. ### Resources Resources are wrappers around database connections and such, providing a cleaner interface to its innards. -Resources are generally registered by plugins. +Resources are generally registered by plugins or local scripts. diff --git a/CHANGELOG.md b/VERSIONS.md similarity index 64% rename from CHANGELOG.md rename to VERSIONS.md index 9b11193..c816f86 100644 --- a/CHANGELOG.md +++ b/VERSIONS.md @@ -1,4 +1,4 @@ -# Changelog +# Versions ## 0.1.0 @@ -8,3 +8,6 @@ - Spark will automatically chdir to the project root when loading the environment. This makes sure scripts and plugins etc all start at a known location. - Added makeself and pharlite in `tools`. +- Rewritten script runner with proper variable substitution. +- Added utility libraries for HTTP requests (Guzzle), templating (Twig) and dotenv + support (symfony/dotenv, activates with environment). diff --git a/composer.json b/composer.json index b7f88ac..d493635 100644 --- a/composer.json +++ b/composer.json @@ -36,6 +36,9 @@ "symfony/process": "^6.0", "psr/log": "^3.0", "symfony/var-dumper": "^6.0", - "symfony/yaml": "^6.0" + "symfony/yaml": "^6.0", + "guzzlehttp/guzzle": "^7.4", + "twig/twig": "^3.3", + "symfony/dotenv": "^6.0" } } diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..21d3ddc --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,26 @@ + + + + + tests + + + + + + src + + + diff --git a/plugins/com.noccy.apiclient/Api/Catalog.php b/plugins/com.noccy.apiclient/Api/Catalog.php new file mode 100644 index 0000000..9de9b70 --- /dev/null +++ b/plugins/com.noccy.apiclient/Api/Catalog.php @@ -0,0 +1,84 @@ +name = $catalog['name']??null; + $this->info = $catalog['info']??null; + foreach ($catalog['props']??[] as $k=>$v) { + $this->properties[$k] = $v; + } + foreach ($catalog['methods']??[] as $k=>$v) { + $this->methods[$k] = new Method($v); + } + } + + public static function createFromFile(string $filename): Catalog + { + $json = file_get_contents($filename); + $catalog = json_decode($json, true); + $catalog['name'] = basename($filename, ".json"); + return new Catalog($catalog); + } + + public function getName(): ?string + { + return $this->name; + } + + public function getInfo(): ?string + { + return $this->info; + } + + public function getProperties(): array + { + return $this->properties; + } + + public function applyProperties(array $props) + { + $this->properties = array_merge($this->properties, $props); + } + + public function addMethod(string $name, Method $method) + { + $this->methods[$name] = $method; + } + + public function getMethod(string $method): ?Method + { + return $this->methods[$method]??null; + } + + public function getMethods(): array + { + return $this->methods; + } + + public function jsonSerialize(): mixed + { + return [ + 'catalog' => [ + 'name' => $this->name, + 'info' => $this->info, + 'props' => $this->properties, + 'methods' => $this->methods, + ] + ]; + } +} diff --git a/plugins/com.noccy.apiclient/Api/Method.php b/plugins/com.noccy.apiclient/Api/Method.php new file mode 100644 index 0000000..0ad4b1a --- /dev/null +++ b/plugins/com.noccy.apiclient/Api/Method.php @@ -0,0 +1,36 @@ +properties = $method['props']??[]; + $this->info = $method['info']??null; + } + + public function getProperties(): array + { + return $this->properties; + } + + public function getInfo(): ?string + { + return $this->info; + } + + public function jsonSerialize(): mixed + { + return [ + 'info' => $this->info, + 'props' => $this->properties, + ]; + } +} diff --git a/plugins/com.noccy.apiclient/Api/Profile.php b/plugins/com.noccy.apiclient/Api/Profile.php new file mode 100644 index 0000000..0b962b3 --- /dev/null +++ b/plugins/com.noccy.apiclient/Api/Profile.php @@ -0,0 +1,13 @@ +properties; + } +} diff --git a/plugins/com.noccy.apiclient/Commands/ApiCatalogCommand.php b/plugins/com.noccy.apiclient/Commands/ApiCatalogCommand.php new file mode 100644 index 0000000..6b5218d --- /dev/null +++ b/plugins/com.noccy.apiclient/Commands/ApiCatalogCommand.php @@ -0,0 +1,91 @@ +setName("api:catalog") + ->setDescription("Manage the API catalogs") + ->addOption("create", "c", InputOption::VALUE_REQUIRED, "Create a new catalog") + ->addOption("remove", "r", InputOption::VALUE_REQUIRED, "Remove a catalog") + ->addOption("set-props", null, InputOption::VALUE_REQUIRED, "Apply properties to a catalog") + ->addArgument("properties", InputArgument::IS_ARRAY, "Default properties for the catalog") + ->addOption("list", null, InputOption::VALUE_NONE, "Only list catalogs, not methods") + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $api = get_plugin('com.noccy.apiclient'); + $list = $input->getOption("list"); + $dest = get_environment()->getConfigDirectory() . "/api/catalogs"; + + if ($create = $input->getOption("create")) { + if (file_exists($dest."/".$create.".json")) { + $output->writeln("Catalog {$create} already exists!"); + return Command::FAILURE; + } + $catalog = new Catalog([ + 'catalog' => [ + 'name' => $create + ] + ]); + file_put_contents($dest."/".$create.".json", json_encode($catalog, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES)); + $output->writeln("Created new catalog {$create}"); + return Command::SUCCESS; + } + if ($remove = $input->getOption("remove")) { + if (!file_exists($dest."/".$remove.".json")) { + $output->writeln("Catalog {$remove} does not exist!"); + return Command::FAILURE; + } + unlink($dest."/".$remove.".json"); + $output->writeln("Removed catalog {$remove}"); + return Command::SUCCESS; + } + if ($setprops = $input->getOption("set-props")) { + $proparr = []; + $props = $input->getArgument("properties"); + foreach ($props as $str) { + if (!str_contains($str,"=")) { + $output->writeln("Ignoring parameter argument '{$str}'"); + } else { + [$k,$v] = explode("=",$str,2); + $proparr[$k] = $v; + } + } + $catalog = $api->getCatalog($setprops); + $catalog->applyProperties($proparr); + $api->saveCatalog($catalog); + $output->writeln("Updated properties on catalog {$setprops}"); + return Command::SUCCESS; + } + + $catalogs = $api->getCatalogNames(); + foreach ($catalogs as $catalog) { + $c = $api->getCatalog($catalog); + if ($list) { + $output->writeln($catalog); + } else { + $output->writeln("\u{25e9} {$catalog}: {$c->getInfo()}"); + $ms = $c->getMethods(); + foreach ($ms as $name=>$m) { + $last = ($m === end($ms)); + $output->writeln(($last?"\u{2514}\u{2500}":"\u{251c}\u{2500}")."\u{25a2} {$catalog}.{$name}: {$m->getInfo()}"); + } + } + } + + return Command::SUCCESS; + } +} diff --git a/plugins/com.noccy.apiclient/Commands/ApiLogsCommand.php b/plugins/com.noccy.apiclient/Commands/ApiLogsCommand.php new file mode 100644 index 0000000..72c9bd3 --- /dev/null +++ b/plugins/com.noccy.apiclient/Commands/ApiLogsCommand.php @@ -0,0 +1,35 @@ +setName("api:logs") + ->setDescription("Show previous requests and manage the log") + ->addOption("clear", null, InputOption::VALUE_NONE, "Clear the log") + ->addOption("write", "w", InputOption::VALUE_REQUIRED, "Write the formatted entries to a file") + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + /** @var ApiClientPlugin */ + $plugin = get_plugin('com.noccy.apiclient'); + + + return Command::SUCCESS; + } +} diff --git a/plugins/com.noccy.apiclient/Commands/ApiProfileCommand.php b/plugins/com.noccy.apiclient/Commands/ApiProfileCommand.php new file mode 100644 index 0000000..5f852f0 --- /dev/null +++ b/plugins/com.noccy.apiclient/Commands/ApiProfileCommand.php @@ -0,0 +1,22 @@ +setName("api:profile") + ->setDescription("Manage API profiles"); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + return Command::SUCCESS; + } +} diff --git a/plugins/com.noccy.apiclient/Commands/ApiRequestCommand.php b/plugins/com.noccy.apiclient/Commands/ApiRequestCommand.php new file mode 100644 index 0000000..d08671a --- /dev/null +++ b/plugins/com.noccy.apiclient/Commands/ApiRequestCommand.php @@ -0,0 +1,134 @@ +setName("api:request") + ->setDescription("Send a request") + ->addOption("profile", "p", InputOption::VALUE_REQUIRED, "Use profile for request") + ->addOption("save", "s", InputOption::VALUE_NONE, "Save to catalog") + ->addArgument("method", InputArgument::OPTIONAL, "Request URL or catalog.method") + ->addArgument("props", InputArgument::IS_ARRAY, "Parameter key=value pairs") + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + /** @var ApiClientPlugin */ + $plugin = get_plugin('com.noccy.apiclient'); + + $separator = str_repeat("\u{2500}", 40); + + $method = $input->getArgument("method"); + $builder = new RequestBuilder(); + if (str_contains($method, "://")) { + $builder->setProperties([ + 'url' => $method + ]); + } else { + if (str_contains($method, '.')) { + [$catalog,$method] = explode(".", $method, 2); + $catalogObj = $plugin->getCatalog($catalog); + // if (!$catalogObj) { + // $output->writeln("No such catalog {$catalog}"); + // return Command::FAILURE; + // } + $methodObj = $catalogObj->getMethod($method); + // if (!$methodObj) { + // $output->writeln("No such method {$method} in catalog {$catalog}"); + // return Command::FAILURE; + // } + $builder->setCatalog($catalogObj); + $builder->setMethod($methodObj); + } + } + $props = []; + $propstr = $input->getArgument("props"); + foreach ($propstr as $str) { + if (!str_contains($str,"=")) { + $output->writeln("Ignoring parameter argument '{$str}'"); + } else { + [$k,$v] = explode("=",$str,2); + $props[$k] = $v; + } + } + $builder->addProperties($props); + + if ($input->getOption("save")) { + $catalogObj = $plugin->getCatalog($catalog); + $methodObj = new Method([ + 'name' => $method, + 'info' => $props['method.info']??null, + 'props' => $props + ]); + + $catalogObj->addMethod($method, $methodObj); + $plugin->saveCatalog($catalogObj); + $output->writeln("Saved method {$method} to catalog {$catalog}"); + return self::SUCCESS; + } + + if ($profile = $input->getOption("profile")) { + $profileObj = $plugin->getProfile($profile); + $builder->setProfile($profileObj); + } + + $request = $builder->getRequest(); + + $table = new Table($output); + $table->setStyle('compact'); + $table->setHeaders([ "Request Info", "" ]); + foreach ($request->getInfo() as $i=>$v) { + $table->addRow([$i,$v]); + } + $table->render(); + + $table = new Table($output); + $table->setStyle('compact'); + $table->setHeaders([ "Request Headers", "" ]); + foreach ($request->getHeaders() as $i=>$v) { + $table->addRow([$i,join("\n",$v)]); + } + $table->render(); + + $output->writeln($separator); + $response = $request->send(); + + $rheaders = $response->getHeaders(); + $table = new Table($output); + $table->setStyle('compact'); + $table->setHeaders([ "Response headers", "" ]); + foreach ($rheaders as $h=>$v) { + $table->addRow([$h,join("\n",$v)]); + } + $table->render(); + $body = (string)$response->getBody(); + $output->writeln($separator); + + $parseAs = $builder->getCalculatedProperty('response.parse'); + if ($parseAs == 'json') { + dump(json_decode($body)); + } else { + $output->writeln($body); + } + + $output->writeln($separator); + $output->writeln(strlen($body)." bytes"); + + return Command::SUCCESS; + } +} diff --git a/plugins/com.noccy.apiclient/README.md b/plugins/com.noccy.apiclient/README.md new file mode 100644 index 0000000..b02d23f --- /dev/null +++ b/plugins/com.noccy.apiclient/README.md @@ -0,0 +1,73 @@ +# ApiClient for Spark + +## Installation + +To install, downlad and extract the plugin directory into your plugin directory. + +## Usage + +*Note: Profiles are not yet implemented* + +You should make a catalog and a profile first. You don't have to, but this will +save you some time. + + $ spark api:catalog --create mysite \ + protocol=http \ + urlbase=http://127.0.0.1:80/api/ + $ spark api:profile --create apiuser \ + --catalog mysite \ + auth.username=apiuser \ + auth.token=APITOKEN + +You can now add some requests: + + $ spark api:request --add mysite.info \ + url=v1/info \ + http.method=POST \ + response.parse=json + +And send them: + + $ spark api:request -p apiuser mysite.info + +## Internals + +ApiClient works on a map of properties, populated with the defaults from the +catalog. The request properties are then appied, followed by the profile +properties. + +### Properties + +``` +# Core properties +protocol={"http"|"websocket"|"xmlrpc"|"jsonrpc"} +# Final URL is [urlbase+]url +urlbase={url} +url={url} + +# Authentication options +auth.username={username} +auth.password={password} +auth.token={token} +auth.type={"basic"|"bearer"} + +# HTTP options +http.method={"GET"|"POST"|...} +http.version={"1.0"|"1.1"|"2.0"} +http.header.{name}={value} +http.query.{field}={value} +http.body={raw-body} +http.body.json={object} + +# RPC options +rpc.method={string} +rpc.argument.{index}={value} + +# Request handling +request.follow-redirecs={"auto"|"no"|"yes"} +request.max-redirects={number} + +# Response handling +response.parse={"none"|"json"|"yaml"|"xml"} +response.good="200,201,202,203,204,205,206,207,208" +``` diff --git a/plugins/com.noccy.apiclient/Request/HttpRequest.php b/plugins/com.noccy.apiclient/Request/HttpRequest.php new file mode 100644 index 0000000..a8b17da --- /dev/null +++ b/plugins/com.noccy.apiclient/Request/HttpRequest.php @@ -0,0 +1,91 @@ +$value) { + + if (str_starts_with($prop, 'http.')) { + $this->handleHttpProp(substr($prop,5), $value); + } elseif ($prop == 'url') { + $this->url = $value; + } + } + } + + private function handleHttpProp(string $prop, $value) + { + + if (str_starts_with($prop, 'query.')) { + $this->query[substr($prop, 6)] = $value; + } elseif (str_starts_with($prop, 'header.')) { + $this->headers[substr($prop, 7)] = $value; + } elseif ($prop === 'method') { + $this->method = strtoupper($value); + } elseif ($prop === 'version') { + $this->version = $value; + } else { + fprintf(STDERR, "Warning: unhandled prop: http.%s (%s)\n", $prop, $value); + } + } + + public function getInfo(): array + { + $query = http_build_query($this->query); + $headers = []; + foreach ($this->headers as $k=>$v) { + // Convert to Proper-Case unless UPPERCASE + if ($k !== strtoupper($k)) + $k = ucwords($k, '-'); + // Build the header + $headers[] = sprintf("%s: %s", $k, $v); + } + return [ + 'protocol' => sprintf("HTTP/%s %s", $this->version, $this->method), + 'query' => $this->url . "?" . $query, + 'body' => "Empty body" + ]; + } + + public function getHeaders(): array + { + $headers = []; + foreach ($this->headers as $k=>$v) { + // Convert to Proper-Case unless UPPERCASE + if ($k !== strtoupper($k)) + $k = ucwords($k, '-'); + // Build the header + $headers[$k] = (array)$v; + } + return $headers; + } + + public function send(): ?Response + { + $query = http_build_query($this->query); + $url = $this->url . ($query?'?'.$query:''); + $config = []; + $client = new Client($config); + $options = []; + $response = $client->request($this->method, $url, $options); + return $response; + } + +} \ No newline at end of file diff --git a/plugins/com.noccy.apiclient/Request/JsonRpcRequest.php b/plugins/com.noccy.apiclient/Request/JsonRpcRequest.php new file mode 100644 index 0000000..8e0fc7c --- /dev/null +++ b/plugins/com.noccy.apiclient/Request/JsonRpcRequest.php @@ -0,0 +1,26 @@ + HttpRequest::class, + 'websocket' => WebSocketRequest::class, + 'jsonrpc' => JsonRpcRequest::class, + ]; + + private ?Catalog $catalog = null; + + private ?Method $method = null; + + private ?Profile $profile = null; + + private array $props = []; + + public function setCatalog(?Catalog $catalog) + { + $this->catalog = $catalog; + return $this; + } + + public function setMethod(?Method $method) + { + $this->method = $method; + return $this; + } + + public function setProfile(?Profile $profile) + { + $this->profile = $profile; + return $this; + } + + public function setProperties(array $properties) + { + $this->props = $properties; + } + + public function addProperties(array $properties) + { + $this->props = array_merge( + $this->props, + $properties + ); + } + + private function buildProperties() + { + $props = []; + if ($this->catalog) { + $add = $this->catalog->getProperties(); + $props = array_merge($props, $add); + } + if ($this->method) { + $add = $this->method->getProperties(); + $props = array_merge($props, $add); + } + if ($this->profile) { + $add = $this->profile->getProperties(); + $props = array_merge($props, $add); + } + $props = array_merge($props, $this->props); + $props = array_filter($props); + return $props; + } + + public function getCalculatedProperty(string $name) + { + $props = $this->buildProperties(); + return $props[$name] ?? null; + } + + public function getRequest(): Request + { + $props = $this->buildProperties(); + $protocol = $props['protocol']??'http'; + + if (!$handler = self::$Protocols[$protocol]??null) { + throw new \Exception("Invalid protocol for request: {$protocol}"); + } + + $base = $props['urlbase']??null; + $url = $props['url']??null; + if ($base) { + $props['url'] = $base . $url; + } + + $request = new $handler($props); + return $request; + } +} \ No newline at end of file diff --git a/plugins/com.noccy.apiclient/Request/WebsocketRequest.php b/plugins/com.noccy.apiclient/Request/WebsocketRequest.php new file mode 100644 index 0000000..17a38ea --- /dev/null +++ b/plugins/com.noccy.apiclient/Request/WebsocketRequest.php @@ -0,0 +1,26 @@ +getConfigDirectory() . "/api/catalogs"; + if (file_exists($catalogDir)) { + $catalogFiles = glob($catalogDir."/*.json"); + foreach ($catalogFiles as $catalogFile) { + $name = basename($catalogFile, ".json"); + $this->catalogs[$name] = Api\Catalog::createFromFile($catalogFile); + } + } + } + + private function loadProfiles() + { + $env = get_environment(); + $catalogDir = $env->getConfigDirectory() . "/api/profiles"; + if (file_exists($catalogDir)) { + $catalogFiles = glob($catalogDir."/*.json"); + foreach ($catalogFiles as $catalogFile) { + + } + } + } + + public function createCatalog(string $name): ?Api\Catalog + { + return null; + } + + public function saveCatalog(Api\Catalog $catalog) + { + $env = get_environment(); + $catalogDir = $env->getConfigDirectory() . "/api/catalogs"; + $catalogFile = $catalogDir . "/" . $catalog->getName() . ".json"; + + if (!is_dir($catalogDir)) { + mkdir($catalogDir, 0777, true); + } + + file_put_contents($catalogFile."~", json_encode($catalog, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES)); + rename($catalogFile."~", $catalogFile); + } + + public function deleteCatalog(string $name) + { + + } + + public function getCatalog(string $name): ?Api\Catalog + { + if (empty($this->catalogs)) $this->loadCatalogs(); + + return $this->catalogs[$name]??null; + } + + public function getCatalogNames(): array + { + if (empty($this->catalogs)) $this->loadCatalogs(); + + return array_keys($this->catalogs); + } + + public function saveProfile(string $name, Api\Profile $profile) + { + + } + + public function deleteProfile(string $name) + { + + } + + public function getProfile(string $name): ?Api\Profile + { + if (empty($this->profiles)) $this->loadProfiles(); + + return null; + } + + public function getProfileNames(): array + { + if (empty($this->profiles)) $this->loadProfiles(); + + return array_keys($this->profiles); + } +} + +register_plugin("com.noccy.apiclient", new ApiClientPlugin); diff --git a/plugins/com.noccy.docker/DockerDbExportCommand.php b/plugins/com.noccy.docker/DockerDbExportCommand.php index 239586e..e7ee42f 100644 --- a/plugins/com.noccy.docker/DockerDbExportCommand.php +++ b/plugins/com.noccy.docker/DockerDbExportCommand.php @@ -65,7 +65,7 @@ class DockerDbExportCommand extends Command $cmd = sprintf("mysqldump -u%s -p%s %s", $dbuser, $dbpass, $database); break; } - $this->exportFromService($service, $cmd, $output); + $this->exportFromService($service, $cmd, $output, $input->getOption("output")); } elseif ($dsn) { $url = parse_url($dsn); if (empty($url)) { @@ -77,9 +77,13 @@ class DockerDbExportCommand extends Command return Command::SUCCESS; } - private function exportFromService(string $service, string $command, OutputInterface $output) + private function exportFromService(string $service, string $command, OutputInterface $output, ?string $outfile=null) { $cmd = sprintf("docker-compose exec -T %s %s", $service, $command); + if ($outfile) { + $cmd .= " > ".escapeshellarg($outfile); + } + $output->writeln(sprintf("→ %s", $cmd)); passthru($cmd); } } diff --git a/src/Commands/PluginsCommand.php b/src/Commands/PluginsCommand.php index 371164a..d72d6b8 100644 --- a/src/Commands/PluginsCommand.php +++ b/src/Commands/PluginsCommand.php @@ -70,10 +70,10 @@ class PluginsCommand extends Command $info = object(); } $installed = in_array(basename($plugin), $localPluginList); - $badge = ($installed)?"\u{2714}":"\u{27f3}"; - $output->writeln(sprintf(" %s %-20s %s", $badge, basename($plugin), $info->name??null)); + $badge = ($installed)?"\u{2714}":"\u{25cc}"; + $output->writeln(sprintf(" %s %-20s %s", $badge, ($installed?"#0ff":"#088"), basename($plugin), $info->name??null)); } return Command::SUCCESS; } -} \ No newline at end of file +} diff --git a/src/Commands/RunCommand.php b/src/Commands/RunCommand.php index ff9d82c..fc6932b 100644 --- a/src/Commands/RunCommand.php +++ b/src/Commands/RunCommand.php @@ -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; diff --git a/src/Environment/Environment.php b/src/Environment/Environment.php index a6ffd50..092d5df 100644 --- a/src/Environment/Environment.php +++ b/src/Environment/Environment.php @@ -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); + } } diff --git a/src/Environment/ScriptRunner.php b/src/Environment/ScriptRunner.php new file mode 100644 index 0000000..1d7ce82 --- /dev/null +++ b/src/Environment/ScriptRunner.php @@ -0,0 +1,91 @@ +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); + } +} diff --git a/src/SparkApplication.php b/src/SparkApplication.php index 31813a9..29a8828 100644 --- a/src/SparkApplication.php +++ b/src/SparkApplication.php @@ -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()); } diff --git a/src/install b/src/install index 01da7ba..dfdb866 100644 --- a/src/install +++ b/src/install @@ -1,5 +1,10 @@ expandString($source); + return $this->assertEquals($expect, $expanded); + } + + public function stringExpansionData() + { + return [ + [ 'Hello World!', 'Hello World!' ], + [ '${testenv}', '' ], + [ '${PATH}', getenv("PATH") ], + [ 'Greetings ${USER}', 'Greetings '.getenv("USER") ], + ]; + } + +} diff --git a/tools/makeself b/tools/makeself deleted file mode 120000 index 57c7955..0000000 --- a/tools/makeself +++ /dev/null @@ -1 +0,0 @@ -makeself-2.4.5/makeself.sh \ No newline at end of file diff --git a/tools/makeself b/tools/makeself new file mode 100755 index 0000000..c8ea565 --- /dev/null +++ b/tools/makeself @@ -0,0 +1,822 @@ +#!/bin/sh +# +# Makeself version 2.4.x +# by Stephane Peter +# +# Utility to create self-extracting tar.gz archives. +# The resulting archive is a file holding the tar.gz archive with +# a small Shell script stub that uncompresses the archive to a temporary +# directory and then executes a given script from withing that directory. +# +# Makeself home page: https://makeself.io/ +# +# Version 2.0 is a rewrite of version 1.0 to make the code easier to read and maintain. +# +# Version history : +# - 1.0 : Initial public release +# - 1.1 : The archive can be passed parameters that will be passed on to +# the embedded script, thanks to John C. Quillan +# - 1.2 : Package distribution, bzip2 compression, more command line options, +# support for non-temporary archives. Ideas thanks to Francois Petitjean +# - 1.3 : More patches from Bjarni R. Einarsson and Francois Petitjean: +# Support for no compression (--nocomp), script is no longer mandatory, +# automatic launch in an xterm, optional verbose output, and -target +# archive option to indicate where to extract the files. +# - 1.4 : Improved UNIX compatibility (Francois Petitjean) +# Automatic integrity checking, support of LSM files (Francois Petitjean) +# - 1.5 : Many bugfixes. Optionally disable xterm spawning. +# - 1.5.1 : More bugfixes, added archive options -list and -check. +# - 1.5.2 : Cosmetic changes to inform the user of what's going on with big +# archives (Quake III demo) +# - 1.5.3 : Check for validity of the DISPLAY variable before launching an xterm. +# More verbosity in xterms and check for embedded command's return value. +# Bugfix for Debian 2.0 systems that have a different "print" command. +# - 1.5.4 : Many bugfixes. Print out a message if the extraction failed. +# - 1.5.5 : More bugfixes. Added support for SETUP_NOCHECK environment variable to +# bypass checksum verification of archives. +# - 1.6.0 : Compute MD5 checksums with the md5sum command (patch from Ryan Gordon) +# - 2.0 : Brand new rewrite, cleaner architecture, separated header and UNIX ports. +# - 2.0.1 : Added --copy +# - 2.1.0 : Allow multiple tarballs to be stored in one archive, and incremental updates. +# Added --nochown for archives +# Stopped doing redundant checksums when not necesary +# - 2.1.1 : Work around insane behavior from certain Linux distros with no 'uncompress' command +# Cleaned up the code to handle error codes from compress. Simplified the extraction code. +# - 2.1.2 : Some bug fixes. Use head -n to avoid problems. +# - 2.1.3 : Bug fixes with command line when spawning terminals. +# Added --tar for archives, allowing to give arbitrary arguments to tar on the contents of the archive. +# Added --noexec to prevent execution of embedded scripts. +# Added --nomd5 and --nocrc to avoid creating checksums in archives. +# Added command used to create the archive in --info output. +# Run the embedded script through eval. +# - 2.1.4 : Fixed --info output. +# Generate random directory name when extracting files to . to avoid problems. (Jason Trent) +# Better handling of errors with wrong permissions for the directory containing the files. (Jason Trent) +# Avoid some race conditions (Ludwig Nussel) +# Unset the $CDPATH variable to avoid problems if it is set. (Debian) +# Better handling of dot files in the archive directory. +# - 2.1.5 : Made the md5sum detection consistent with the header code. +# Check for the presence of the archive directory +# Added --encrypt for symmetric encryption through gpg (Eric Windisch) +# Added support for the digest command on Solaris 10 for MD5 checksums +# Check for available disk space before extracting to the target directory (Andreas Schweitzer) +# Allow extraction to run asynchronously (patch by Peter Hatch) +# Use file descriptors internally to avoid error messages (patch by Kay Tiong Khoo) +# - 2.1.6 : Replaced one dot per file progress with a realtime progress percentage and a spining cursor (Guy Baconniere) +# Added --noprogress to prevent showing the progress during the decompression (Guy Baconniere) +# Added --target dir to allow extracting directly to a target directory (Guy Baconniere) +# - 2.2.0 : Many bugfixes, updates and contributions from users. Check out the project page on Github for the details. +# - 2.3.0 : Option to specify packaging date to enable byte-for-byte reproducibility. (Marc Pawlowsky) +# - 2.4.0 : Optional support for SHA256 checksums in archives. +# - 2.4.2 : Add support for threads for several compressors. (M. Limber) +# Added zstd support. +# - 2.4.3 : Make explicit POSIX tar archives for increased compatibility. +# - 2.4.5 : Added --tar-format to override ustar tar archive format +# +# (C) 1998-2021 by Stephane Peter +# +# This software is released under the terms of the GNU GPL version 2 and above +# Please read the license at http://www.gnu.org/copyleft/gpl.html +# Self-extracting archives created with this script are explictly NOT released under the term of the GPL +# + +MS_VERSION=2.4.5 +MS_COMMAND="$0" +unset CDPATH + +for f in ${1+"$@"}; do + MS_COMMAND="$MS_COMMAND \\\\ + \\\"$f\\\"" +done + +# For Solaris systems +if test -d /usr/xpg4/bin; then + PATH=/usr/xpg4/bin:$PATH + export PATH +fi + +# Procedures + +MS_Usage() +{ + echo "Usage: $0 [args] archive_dir file_name label startup_script [script_args]" + echo "args can be one or more of the following :" + echo " --version | -v : Print out Makeself version number and exit" + echo " --help | -h : Print out this help message" + echo " --tar-quietly : Suppress verbose output from the tar command" + echo " --quiet | -q : Do not print any messages other than errors." + echo " --gzip : Compress using gzip (default if detected)" + echo " --pigz : Compress with pigz" + echo " --zstd : Compress with zstd" + echo " --bzip2 : Compress using bzip2 instead of gzip" + echo " --pbzip2 : Compress using pbzip2 instead of gzip" + echo " --xz : Compress using xz instead of gzip" + echo " --lzo : Compress using lzop instead of gzip" + echo " --lz4 : Compress using lz4 instead of gzip" + echo " --compress : Compress using the UNIX 'compress' command" + echo " --complevel lvl : Compression level for gzip pigz zstd xz lzo lz4 bzip2 and pbzip2 (default 9)" + echo " --threads thds : Number of threads to be used by compressors that support parallelization." + echo " Omit to use compressor's default. Most useful (and required) for opting" + echo " into xz's threading, usually with '--threads=0' for all available cores." + echo " pbzip2 and pigz are parallel by default, and setting this value allows" + echo " limiting the number of threads they use." + echo " --base64 : Instead of compressing, encode the data using base64" + echo " --gpg-encrypt : Instead of compressing, encrypt the data using GPG" + echo " --gpg-asymmetric-encrypt-sign" + echo " : Instead of compressing, asymmetrically encrypt and sign the data using GPG" + echo " --gpg-extra opt : Append more options to the gpg command line" + echo " --ssl-encrypt : Instead of compressing, encrypt the data using OpenSSL" + echo " --ssl-passwd pass : Use the given password to encrypt the data using OpenSSL" + echo " --ssl-pass-src src : Use the given src as the source of password to encrypt the data" + echo " using OpenSSL. See \"PASS PHRASE ARGUMENTS\" in man openssl." + echo " If this option is not supplied, the user will be asked to enter" + echo " encryption password on the current terminal." + echo " --ssl-no-md : Do not use \"-md\" option not supported by older OpenSSL." + echo " --nochown : Do not give the target folder to the current user (default)" + echo " --chown : Give the target folder to the current user recursively" + echo " --nocomp : Do not compress the data" + echo " --notemp : The archive will create archive_dir in the" + echo " current directory and uncompress in ./archive_dir" + echo " --needroot : Check that the root user is extracting the archive before proceeding" + echo " --copy : Upon extraction, the archive will first copy itself to" + echo " a temporary directory" + echo " --append : Append more files to an existing Makeself archive" + echo " The label and startup scripts will then be ignored" + echo " --target dir : Extract directly to a target directory" + echo " directory path can be either absolute or relative" + echo " --nooverwrite : Do not extract the archive if the specified target directory exists" + echo " --current : Files will be extracted to the current directory" + echo " Both --current and --target imply --notemp" + echo " --tar-format opt : Specify a tar archive format (default is ustar)" + echo " --tar-extra opt : Append more options to the tar command line" + echo " --untar-extra opt : Append more options to the during the extraction of the tar archive" + echo " --nomd5 : Don't calculate an MD5 for archive" + echo " --nocrc : Don't calculate a CRC for archive" + echo " --sha256 : Compute a SHA256 checksum for the archive" + echo " --header file : Specify location of the header script" + echo " --cleanup file : Specify a cleanup script that executes on interrupt and when finished successfully." + echo " --follow : Follow the symlinks in the archive" + echo " --noprogress : Do not show the progress during the decompression" + echo " --nox11 : Disable automatic spawn of a xterm" + echo " --nowait : Do not wait for user input after executing embedded" + echo " program from an xterm" + echo " --sign passphrase : Signature private key to sign the package with" + echo " --lsm file : LSM file describing the package" + echo " --license file : Append a license file" + echo " --help-header file : Add a header to the archive's --help output" + echo " --packaging-date date" + echo " : Use provided string as the packaging date" + echo " instead of the current date." + echo + echo " --keep-umask : Keep the umask set to shell default, rather than overriding when executing self-extracting archive." + echo " --export-conf : Export configuration variables to startup_script" + echo + echo "Do not forget to give a fully qualified startup script name" + echo "(i.e. with a ./ prefix if inside the archive)." + exit 1 +} + +# Default settings +if type gzip >/dev/null 2>&1; then + COMPRESS=gzip +elif type compress >/dev/null 2>&1; then + COMPRESS=compress +else + echo "ERROR: missing commands: gzip, compress" >&2 + MS_Usage +fi +ENCRYPT=n +PASSWD="" +PASSWD_SRC="" +OPENSSL_NO_MD=n +COMPRESS_LEVEL=9 +DEFAULT_THREADS=123456 # Sentinel value +THREADS=$DEFAULT_THREADS +KEEP=n +CURRENT=n +NOX11=n +NOWAIT=n +APPEND=n +TAR_QUIETLY=n +KEEP_UMASK=n +QUIET=n +NOPROGRESS=n +COPY=none +NEED_ROOT=n +TAR_ARGS=rvf +TAR_FORMAT=ustar +TAR_EXTRA="" +GPG_EXTRA="" +DU_ARGS=-ks +HEADER=`dirname "$0"`/makeself-header.sh +SIGNATURE="" +TARGETDIR="" +NOOVERWRITE=n +DATE=`LC_ALL=C date` +EXPORT_CONF=n +SHA256=n +OWNERSHIP=n +SIGN=n +GPG_PASSPHRASE="" + +# LSM file stuff +LSM_CMD="echo No LSM. >> \"\$archname\"" + +while true +do + case "$1" in + --version | -v) + echo Makeself version $MS_VERSION + exit 0 + ;; + --pbzip2) + COMPRESS=pbzip2 + shift + ;; + --bzip2) + COMPRESS=bzip2 + shift + ;; + --gzip) + COMPRESS=gzip + shift + ;; + --pigz) + COMPRESS=pigz + shift + ;; + --zstd) + COMPRESS=zstd + shift + ;; + --xz) + COMPRESS=xz + shift + ;; + --lzo) + COMPRESS=lzo + shift + ;; + --lz4) + COMPRESS=lz4 + shift + ;; + --compress) + COMPRESS=compress + shift + ;; + --base64) + COMPRESS=base64 + shift + ;; + --gpg-encrypt) + COMPRESS=gpg + shift + ;; + --gpg-asymmetric-encrypt-sign) + COMPRESS=gpg-asymmetric + shift + ;; + --gpg-extra) + GPG_EXTRA="$2" + shift 2 || { MS_Usage; exit 1; } + ;; + --ssl-encrypt) + ENCRYPT=openssl + shift + ;; + --ssl-passwd) + PASSWD=$2 + shift 2 || { MS_Usage; exit 1; } + ;; + --ssl-pass-src) + PASSWD_SRC=$2 + shift 2 || { MS_Usage; exit 1; } + ;; + --ssl-no-md) + OPENSSL_NO_MD=y + shift + ;; + --nocomp) + COMPRESS=none + shift + ;; + --complevel) + COMPRESS_LEVEL="$2" + shift 2 || { MS_Usage; exit 1; } + ;; + --threads) + THREADS="$2" + shift 2 || { MS_Usage; exit 1; } + ;; + --nochown) + OWNERSHIP=n + shift + ;; + --chown) + OWNERSHIP=y + shift + ;; + --notemp) + KEEP=y + shift + ;; + --copy) + COPY=copy + shift + ;; + --current) + CURRENT=y + KEEP=y + shift + ;; + --tar-format) + TAR_FORMAT="$2" + shift 2 || { MS_Usage; exit 1; } + ;; + --tar-extra) + TAR_EXTRA="$2" + shift 2 || { MS_Usage; exit 1; } + ;; + --untar-extra) + UNTAR_EXTRA="$2" + shift 2 || { MS_Usage; exit 1; } + ;; + --target) + TARGETDIR="$2" + KEEP=y + shift 2 || { MS_Usage; exit 1; } + ;; + --sign) + SIGN=y + GPG_PASSPHRASE="$2" + shift 2 || { MS_Usage; exit 1; } + ;; + --nooverwrite) + NOOVERWRITE=y + shift + ;; + --needroot) + NEED_ROOT=y + shift + ;; + --header) + HEADER="$2" + shift 2 || { MS_Usage; exit 1; } + ;; + --cleanup) + CLEANUP_SCRIPT="$2" + shift 2 || { MS_Usage; exit 1; } + ;; + --license) + # We need to escape all characters having a special meaning in double quotes + LICENSE=$(sed 's/\\/\\\\/g; s/"/\\\"/g; s/`/\\\`/g; s/\$/\\\$/g' "$2") + shift 2 || { MS_Usage; exit 1; } + ;; + --follow) + TAR_ARGS=rvhf + DU_ARGS=-ksL + shift + ;; + --noprogress) + NOPROGRESS=y + shift + ;; + --nox11) + NOX11=y + shift + ;; + --nowait) + NOWAIT=y + shift + ;; + --nomd5) + NOMD5=y + shift + ;; + --sha256) + SHA256=y + shift + ;; + --nocrc) + NOCRC=y + shift + ;; + --append) + APPEND=y + shift + ;; + --lsm) + LSM_CMD="cat \"$2\" >> \"\$archname\"" + shift 2 || { MS_Usage; exit 1; } + ;; + --packaging-date) + DATE="$2" + shift 2 || { MS_Usage; exit 1; } + ;; + --help-header) + HELPHEADER=`sed -e "s/'/'\\\\\''/g" $2` + shift 2 || { MS_Usage; exit 1; } + [ -n "$HELPHEADER" ] && HELPHEADER="$HELPHEADER +" + ;; + --tar-quietly) + TAR_QUIETLY=y + shift + ;; + --keep-umask) + KEEP_UMASK=y + shift + ;; + --export-conf) + EXPORT_CONF=y + shift + ;; + -q | --quiet) + QUIET=y + shift + ;; + -h | --help) + MS_Usage + ;; + -*) + echo Unrecognized flag : "$1" + MS_Usage + ;; + *) + break + ;; + esac +done + +if test $# -lt 1; then + MS_Usage +else + if test -d "$1"; then + archdir="$1" + else + echo "Directory $1 does not exist." >&2 + exit 1 + fi +fi +archname="$2" + +if test "$QUIET" = "y" || test "$TAR_QUIETLY" = "y"; then + if test "$TAR_ARGS" = "rvf"; then + TAR_ARGS="rf" + elif test "$TAR_ARGS" = "rvhf"; then + TAR_ARGS="rhf" + fi +fi + +if test "$APPEND" = y; then + if test $# -lt 2; then + MS_Usage + fi + + # Gather the info from the original archive + OLDENV=`sh "$archname" --dumpconf` + if test $? -ne 0; then + echo "Unable to update archive: $archname" >&2 + exit 1 + else + eval "$OLDENV" + OLDSKIP=`expr $SKIP + 1` + fi +else + if test "$KEEP" = n -a $# = 3; then + echo "ERROR: Making a temporary archive with no embedded command does not make sense!" >&2 + echo >&2 + MS_Usage + fi + # We don't want to create an absolute directory unless a target directory is defined + if test "$CURRENT" = y; then + archdirname="." + elif test x"$TARGETDIR" != x; then + archdirname="$TARGETDIR" + else + archdirname=`basename "$1"` + fi + + if test $# -lt 3; then + MS_Usage + fi + + LABEL="$3" + SCRIPT="$4" + test "x$SCRIPT" = x || shift 1 + shift 3 + SCRIPTARGS="$*" +fi + +if test "$KEEP" = n -a "$CURRENT" = y; then + echo "ERROR: It is A VERY DANGEROUS IDEA to try to combine --notemp and --current." >&2 + exit 1 +fi + +case $COMPRESS in +gzip) + GZIP_CMD="gzip -c$COMPRESS_LEVEL" + GUNZIP_CMD="gzip -cd" + ;; +pigz) + GZIP_CMD="pigz -$COMPRESS_LEVEL" + if test $THREADS -ne $DEFAULT_THREADS; then # Leave as the default if threads not indicated + GZIP_CMD="$GZIP_CMD --processes $THREADS" + fi + GUNZIP_CMD="gzip -cd" + ;; +zstd) + GZIP_CMD="zstd -$COMPRESS_LEVEL" + if test $THREADS -ne $DEFAULT_THREADS; then # Leave as the default if threads not indicated + GZIP_CMD="$GZIP_CMD --threads=$THREADS" + fi + GUNZIP_CMD="zstd -cd" + ;; +pbzip2) + GZIP_CMD="pbzip2 -c$COMPRESS_LEVEL" + if test $THREADS -ne $DEFAULT_THREADS; then # Leave as the default if threads not indicated + GZIP_CMD="$GZIP_CMD -p$THREADS" + fi + GUNZIP_CMD="bzip2 -d" + ;; +bzip2) + GZIP_CMD="bzip2 -$COMPRESS_LEVEL" + GUNZIP_CMD="bzip2 -d" + ;; +xz) + GZIP_CMD="xz -c$COMPRESS_LEVEL" + # Must opt-in by specifying a value since not all versions of xz support threads + if test $THREADS -ne $DEFAULT_THREADS; then + GZIP_CMD="$GZIP_CMD --threads=$THREADS" + fi + GUNZIP_CMD="xz -d" + ;; +lzo) + GZIP_CMD="lzop -c$COMPRESS_LEVEL" + GUNZIP_CMD="lzop -d" + ;; +lz4) + GZIP_CMD="lz4 -c$COMPRESS_LEVEL" + GUNZIP_CMD="lz4 -d" + ;; +base64) + GZIP_CMD="base64" + GUNZIP_CMD="base64 --decode -i -" + ;; +gpg) + GZIP_CMD="gpg $GPG_EXTRA -ac -z$COMPRESS_LEVEL" + GUNZIP_CMD="gpg -d" + ENCRYPT="gpg" + ;; +gpg-asymmetric) + GZIP_CMD="gpg $GPG_EXTRA -z$COMPRESS_LEVEL -es" + GUNZIP_CMD="gpg --yes -d" + ENCRYPT="gpg" + ;; +compress) + GZIP_CMD="compress -fc" + GUNZIP_CMD="(type compress >/dev/null 2>&1 && compress -fcd || gzip -cd)" + ;; +none) + GZIP_CMD="cat" + GUNZIP_CMD="cat" + ;; +esac + +if test x"$ENCRYPT" = x"openssl"; then + if test x"$APPEND" = x"y"; then + echo "Appending to existing archive is not compatible with OpenSSL encryption." >&2 + fi + + ENCRYPT_CMD="openssl enc -aes-256-cbc -salt" + DECRYPT_CMD="openssl enc -aes-256-cbc -d" + + if test x"$OPENSSL_NO_MD" != x"y"; then + ENCRYPT_CMD="$ENCRYPT_CMD -md sha256" + DECRYPT_CMD="$DECRYPT_CMD -md sha256" + fi + + if test -n "$PASSWD_SRC"; then + ENCRYPT_CMD="$ENCRYPT_CMD -pass $PASSWD_SRC" + elif test -n "$PASSWD"; then + ENCRYPT_CMD="$ENCRYPT_CMD -pass pass:$PASSWD" + fi +fi + +tmpfile="${TMPDIR:-/tmp}/mkself$$" + +if test -f "$HEADER"; then + oldarchname="$archname" + archname="$tmpfile" + # Generate a fake header to count its lines + SKIP=0 + . "$HEADER" + SKIP=`cat "$tmpfile" |wc -l` + # Get rid of any spaces + SKIP=`expr $SKIP` + rm -f "$tmpfile" + if test "$QUIET" = "n"; then + echo "Header is $SKIP lines long" >&2 + fi + archname="$oldarchname" +else + echo "Unable to open header file: $HEADER" >&2 + exit 1 +fi + +if test "$QUIET" = "n"; then + echo +fi + +if test "$APPEND" = n; then + if test -f "$archname"; then + echo "WARNING: Overwriting existing file: $archname" >&2 + fi +fi + +USIZE=`du $DU_ARGS "$archdir" | awk '{print $1}'` + +if test "." = "$archdirname"; then + if test "$KEEP" = n; then + archdirname="makeself-$$-`date +%Y%m%d%H%M%S`" + fi +fi + +test -d "$archdir" || { echo "Error: $archdir does not exist."; rm -f "$tmpfile"; exit 1; } +if test "$QUIET" = "n"; then + echo "About to compress $USIZE KB of data..." + echo "Adding files to archive named \"$archname\"..." +fi + +# See if we have GNU tar +TAR=`exec <&- 2>&-; which gtar || command -v gtar || type gtar` +test -x "$TAR" || TAR=tar + +tmparch="${TMPDIR:-/tmp}/mkself$$.tar" +( + if test "$APPEND" = "y"; then + tail -n "+$OLDSKIP" "$archname" | eval "$GUNZIP_CMD" > "$tmparch" + fi + cd "$archdir" + # "Determining if a directory is empty" + # https://www.etalabs.net/sh_tricks.html + find . \ + \( \ + ! -type d \ + -o \ + \( -links 2 -exec sh -c ' + is_empty () ( + cd "$1" + set -- .[!.]* ; test -f "$1" && return 1 + set -- ..?* ; test -f "$1" && return 1 + set -- * ; test -f "$1" && return 1 + return 0 + ) + is_empty "$0"' {} \; \ + \) \ + \) -print \ + | LC_ALL=C sort \ + | sed 's/./\\&/g' \ + | xargs $TAR $TAR_EXTRA --format $TAR_FORMAT -$TAR_ARGS "$tmparch" +) || { + echo "ERROR: failed to create temporary archive: $tmparch" + rm -f "$tmparch" "$tmpfile" + exit 1 +} + +USIZE=`du $DU_ARGS "$tmparch" | awk '{print $1}'` + +eval "$GZIP_CMD" <"$tmparch" >"$tmpfile" || { + echo "ERROR: failed to create temporary file: $tmpfile" + rm -f "$tmparch" "$tmpfile" + exit 1 +} +rm -f "$tmparch" + +if test x"$ENCRYPT" = x"openssl"; then + echo "About to encrypt archive \"$archname\"..." + { eval "$ENCRYPT_CMD -in $tmpfile -out ${tmpfile}.enc" && mv -f ${tmpfile}.enc $tmpfile; } || \ + { echo Aborting: could not encrypt temporary file: "$tmpfile".; rm -f "$tmpfile"; exit 1; } +fi + +fsize=`cat "$tmpfile" | wc -c | tr -d " "` + +# Compute the checksums + +shasum=0000000000000000000000000000000000000000000000000000000000000000 +md5sum=00000000000000000000000000000000 +crcsum=0000000000 + +if test "$NOCRC" = y; then + if test "$QUIET" = "n"; then + echo "skipping crc at user request" + fi +else + crcsum=`CMD_ENV=xpg4 cksum < "$tmpfile" | sed -e 's/ /Z/' -e 's/ /Z/' | cut -dZ -f1` + if test "$QUIET" = "n"; then + echo "CRC: $crcsum" + fi +fi + +if test "$SHA256" = y; then + SHA_PATH=`exec <&- 2>&-; which shasum || command -v shasum || type shasum` + if test -x "$SHA_PATH"; then + shasum=`eval "$SHA_PATH -a 256" < "$tmpfile" | cut -b-64` + else + SHA_PATH=`exec <&- 2>&-; which sha256sum || command -v sha256sum || type sha256sum` + shasum=`eval "$SHA_PATH" < "$tmpfile" | cut -b-64` + fi + if test "$QUIET" = "n"; then + if test -x "$SHA_PATH"; then + echo "SHA256: $shasum" + else + echo "SHA256: none, SHA command not found" + fi + fi +fi +if test "$NOMD5" = y; then + if test "$QUIET" = "n"; then + echo "Skipping md5sum at user request" + fi +else + # Try to locate a MD5 binary + OLD_PATH=$PATH + PATH=${GUESS_MD5_PATH:-"$OLD_PATH:/bin:/usr/bin:/sbin:/usr/local/ssl/bin:/usr/local/bin:/opt/openssl/bin"} + MD5_ARG="" + MD5_PATH=`exec <&- 2>&-; which md5sum || command -v md5sum || type md5sum` + test -x "$MD5_PATH" || MD5_PATH=`exec <&- 2>&-; which md5 || command -v md5 || type md5` + test -x "$MD5_PATH" || MD5_PATH=`exec <&- 2>&-; which digest || command -v digest || type digest` + PATH=$OLD_PATH + if test -x "$MD5_PATH"; then + if test `basename ${MD5_PATH}`x = digestx; then + MD5_ARG="-a md5" + fi + md5sum=`eval "$MD5_PATH $MD5_ARG" < "$tmpfile" | cut -b-32` + if test "$QUIET" = "n"; then + echo "MD5: $md5sum" + fi + else + if test "$QUIET" = "n"; then + echo "MD5: none, MD5 command not found" + fi + fi +fi +if test "$SIGN" = y; then + GPG_PATH=`exec <&- 2>&-; which gpg || command -v gpg || type gpg` + if test -x "$GPG_PATH"; then + SIGNATURE=`$GPG_PATH --pinentry-mode=loopback --batch --yes --passphrase "$GPG_PASSPHRASE" --output - --detach-sig $tmpfile | base64 | tr -d \\\\n` + if test "$QUIET" = "n"; then + echo "Signature: $SIGNATURE" + fi + else + echo "Missing gpg command" >&2 + fi +fi + +totalsize=0 +for size in $fsize; +do + totalsize=`expr $totalsize + $size` +done + +if test "$APPEND" = y; then + mv "$archname" "$archname".bak || exit + + # Prepare entry for new archive + filesizes="$fsize" + CRCsum="$crcsum" + MD5sum="$md5sum" + SHAsum="$shasum" + Signature="$SIGNATURE" + # Generate the header + . "$HEADER" + # Append the new data + cat "$tmpfile" >> "$archname" + + chmod +x "$archname" + rm -f "$archname".bak + if test "$QUIET" = "n"; then + echo "Self-extractable archive \"$archname\" successfully updated." + fi +else + filesizes="$fsize" + CRCsum="$crcsum" + MD5sum="$md5sum" + SHAsum="$shasum" + Signature="$SIGNATURE" + + # Generate the header + . "$HEADER" + + # Append the compressed tar data after the stub + if test "$QUIET" = "n"; then + echo + fi + cat "$tmpfile" >> "$archname" + chmod +x "$archname" + if test "$QUIET" = "n"; then + echo Self-extractable archive \"$archname\" successfully created. + fi +fi +rm -f "$tmpfile" diff --git a/tools/makeself-2.4.5/makeself.sh b/tools/makeself-2.4.5/makeself.sh deleted file mode 100755 index c8ea565..0000000 --- a/tools/makeself-2.4.5/makeself.sh +++ /dev/null @@ -1,822 +0,0 @@ -#!/bin/sh -# -# Makeself version 2.4.x -# by Stephane Peter -# -# Utility to create self-extracting tar.gz archives. -# The resulting archive is a file holding the tar.gz archive with -# a small Shell script stub that uncompresses the archive to a temporary -# directory and then executes a given script from withing that directory. -# -# Makeself home page: https://makeself.io/ -# -# Version 2.0 is a rewrite of version 1.0 to make the code easier to read and maintain. -# -# Version history : -# - 1.0 : Initial public release -# - 1.1 : The archive can be passed parameters that will be passed on to -# the embedded script, thanks to John C. Quillan -# - 1.2 : Package distribution, bzip2 compression, more command line options, -# support for non-temporary archives. Ideas thanks to Francois Petitjean -# - 1.3 : More patches from Bjarni R. Einarsson and Francois Petitjean: -# Support for no compression (--nocomp), script is no longer mandatory, -# automatic launch in an xterm, optional verbose output, and -target -# archive option to indicate where to extract the files. -# - 1.4 : Improved UNIX compatibility (Francois Petitjean) -# Automatic integrity checking, support of LSM files (Francois Petitjean) -# - 1.5 : Many bugfixes. Optionally disable xterm spawning. -# - 1.5.1 : More bugfixes, added archive options -list and -check. -# - 1.5.2 : Cosmetic changes to inform the user of what's going on with big -# archives (Quake III demo) -# - 1.5.3 : Check for validity of the DISPLAY variable before launching an xterm. -# More verbosity in xterms and check for embedded command's return value. -# Bugfix for Debian 2.0 systems that have a different "print" command. -# - 1.5.4 : Many bugfixes. Print out a message if the extraction failed. -# - 1.5.5 : More bugfixes. Added support for SETUP_NOCHECK environment variable to -# bypass checksum verification of archives. -# - 1.6.0 : Compute MD5 checksums with the md5sum command (patch from Ryan Gordon) -# - 2.0 : Brand new rewrite, cleaner architecture, separated header and UNIX ports. -# - 2.0.1 : Added --copy -# - 2.1.0 : Allow multiple tarballs to be stored in one archive, and incremental updates. -# Added --nochown for archives -# Stopped doing redundant checksums when not necesary -# - 2.1.1 : Work around insane behavior from certain Linux distros with no 'uncompress' command -# Cleaned up the code to handle error codes from compress. Simplified the extraction code. -# - 2.1.2 : Some bug fixes. Use head -n to avoid problems. -# - 2.1.3 : Bug fixes with command line when spawning terminals. -# Added --tar for archives, allowing to give arbitrary arguments to tar on the contents of the archive. -# Added --noexec to prevent execution of embedded scripts. -# Added --nomd5 and --nocrc to avoid creating checksums in archives. -# Added command used to create the archive in --info output. -# Run the embedded script through eval. -# - 2.1.4 : Fixed --info output. -# Generate random directory name when extracting files to . to avoid problems. (Jason Trent) -# Better handling of errors with wrong permissions for the directory containing the files. (Jason Trent) -# Avoid some race conditions (Ludwig Nussel) -# Unset the $CDPATH variable to avoid problems if it is set. (Debian) -# Better handling of dot files in the archive directory. -# - 2.1.5 : Made the md5sum detection consistent with the header code. -# Check for the presence of the archive directory -# Added --encrypt for symmetric encryption through gpg (Eric Windisch) -# Added support for the digest command on Solaris 10 for MD5 checksums -# Check for available disk space before extracting to the target directory (Andreas Schweitzer) -# Allow extraction to run asynchronously (patch by Peter Hatch) -# Use file descriptors internally to avoid error messages (patch by Kay Tiong Khoo) -# - 2.1.6 : Replaced one dot per file progress with a realtime progress percentage and a spining cursor (Guy Baconniere) -# Added --noprogress to prevent showing the progress during the decompression (Guy Baconniere) -# Added --target dir to allow extracting directly to a target directory (Guy Baconniere) -# - 2.2.0 : Many bugfixes, updates and contributions from users. Check out the project page on Github for the details. -# - 2.3.0 : Option to specify packaging date to enable byte-for-byte reproducibility. (Marc Pawlowsky) -# - 2.4.0 : Optional support for SHA256 checksums in archives. -# - 2.4.2 : Add support for threads for several compressors. (M. Limber) -# Added zstd support. -# - 2.4.3 : Make explicit POSIX tar archives for increased compatibility. -# - 2.4.5 : Added --tar-format to override ustar tar archive format -# -# (C) 1998-2021 by Stephane Peter -# -# This software is released under the terms of the GNU GPL version 2 and above -# Please read the license at http://www.gnu.org/copyleft/gpl.html -# Self-extracting archives created with this script are explictly NOT released under the term of the GPL -# - -MS_VERSION=2.4.5 -MS_COMMAND="$0" -unset CDPATH - -for f in ${1+"$@"}; do - MS_COMMAND="$MS_COMMAND \\\\ - \\\"$f\\\"" -done - -# For Solaris systems -if test -d /usr/xpg4/bin; then - PATH=/usr/xpg4/bin:$PATH - export PATH -fi - -# Procedures - -MS_Usage() -{ - echo "Usage: $0 [args] archive_dir file_name label startup_script [script_args]" - echo "args can be one or more of the following :" - echo " --version | -v : Print out Makeself version number and exit" - echo " --help | -h : Print out this help message" - echo " --tar-quietly : Suppress verbose output from the tar command" - echo " --quiet | -q : Do not print any messages other than errors." - echo " --gzip : Compress using gzip (default if detected)" - echo " --pigz : Compress with pigz" - echo " --zstd : Compress with zstd" - echo " --bzip2 : Compress using bzip2 instead of gzip" - echo " --pbzip2 : Compress using pbzip2 instead of gzip" - echo " --xz : Compress using xz instead of gzip" - echo " --lzo : Compress using lzop instead of gzip" - echo " --lz4 : Compress using lz4 instead of gzip" - echo " --compress : Compress using the UNIX 'compress' command" - echo " --complevel lvl : Compression level for gzip pigz zstd xz lzo lz4 bzip2 and pbzip2 (default 9)" - echo " --threads thds : Number of threads to be used by compressors that support parallelization." - echo " Omit to use compressor's default. Most useful (and required) for opting" - echo " into xz's threading, usually with '--threads=0' for all available cores." - echo " pbzip2 and pigz are parallel by default, and setting this value allows" - echo " limiting the number of threads they use." - echo " --base64 : Instead of compressing, encode the data using base64" - echo " --gpg-encrypt : Instead of compressing, encrypt the data using GPG" - echo " --gpg-asymmetric-encrypt-sign" - echo " : Instead of compressing, asymmetrically encrypt and sign the data using GPG" - echo " --gpg-extra opt : Append more options to the gpg command line" - echo " --ssl-encrypt : Instead of compressing, encrypt the data using OpenSSL" - echo " --ssl-passwd pass : Use the given password to encrypt the data using OpenSSL" - echo " --ssl-pass-src src : Use the given src as the source of password to encrypt the data" - echo " using OpenSSL. See \"PASS PHRASE ARGUMENTS\" in man openssl." - echo " If this option is not supplied, the user will be asked to enter" - echo " encryption password on the current terminal." - echo " --ssl-no-md : Do not use \"-md\" option not supported by older OpenSSL." - echo " --nochown : Do not give the target folder to the current user (default)" - echo " --chown : Give the target folder to the current user recursively" - echo " --nocomp : Do not compress the data" - echo " --notemp : The archive will create archive_dir in the" - echo " current directory and uncompress in ./archive_dir" - echo " --needroot : Check that the root user is extracting the archive before proceeding" - echo " --copy : Upon extraction, the archive will first copy itself to" - echo " a temporary directory" - echo " --append : Append more files to an existing Makeself archive" - echo " The label and startup scripts will then be ignored" - echo " --target dir : Extract directly to a target directory" - echo " directory path can be either absolute or relative" - echo " --nooverwrite : Do not extract the archive if the specified target directory exists" - echo " --current : Files will be extracted to the current directory" - echo " Both --current and --target imply --notemp" - echo " --tar-format opt : Specify a tar archive format (default is ustar)" - echo " --tar-extra opt : Append more options to the tar command line" - echo " --untar-extra opt : Append more options to the during the extraction of the tar archive" - echo " --nomd5 : Don't calculate an MD5 for archive" - echo " --nocrc : Don't calculate a CRC for archive" - echo " --sha256 : Compute a SHA256 checksum for the archive" - echo " --header file : Specify location of the header script" - echo " --cleanup file : Specify a cleanup script that executes on interrupt and when finished successfully." - echo " --follow : Follow the symlinks in the archive" - echo " --noprogress : Do not show the progress during the decompression" - echo " --nox11 : Disable automatic spawn of a xterm" - echo " --nowait : Do not wait for user input after executing embedded" - echo " program from an xterm" - echo " --sign passphrase : Signature private key to sign the package with" - echo " --lsm file : LSM file describing the package" - echo " --license file : Append a license file" - echo " --help-header file : Add a header to the archive's --help output" - echo " --packaging-date date" - echo " : Use provided string as the packaging date" - echo " instead of the current date." - echo - echo " --keep-umask : Keep the umask set to shell default, rather than overriding when executing self-extracting archive." - echo " --export-conf : Export configuration variables to startup_script" - echo - echo "Do not forget to give a fully qualified startup script name" - echo "(i.e. with a ./ prefix if inside the archive)." - exit 1 -} - -# Default settings -if type gzip >/dev/null 2>&1; then - COMPRESS=gzip -elif type compress >/dev/null 2>&1; then - COMPRESS=compress -else - echo "ERROR: missing commands: gzip, compress" >&2 - MS_Usage -fi -ENCRYPT=n -PASSWD="" -PASSWD_SRC="" -OPENSSL_NO_MD=n -COMPRESS_LEVEL=9 -DEFAULT_THREADS=123456 # Sentinel value -THREADS=$DEFAULT_THREADS -KEEP=n -CURRENT=n -NOX11=n -NOWAIT=n -APPEND=n -TAR_QUIETLY=n -KEEP_UMASK=n -QUIET=n -NOPROGRESS=n -COPY=none -NEED_ROOT=n -TAR_ARGS=rvf -TAR_FORMAT=ustar -TAR_EXTRA="" -GPG_EXTRA="" -DU_ARGS=-ks -HEADER=`dirname "$0"`/makeself-header.sh -SIGNATURE="" -TARGETDIR="" -NOOVERWRITE=n -DATE=`LC_ALL=C date` -EXPORT_CONF=n -SHA256=n -OWNERSHIP=n -SIGN=n -GPG_PASSPHRASE="" - -# LSM file stuff -LSM_CMD="echo No LSM. >> \"\$archname\"" - -while true -do - case "$1" in - --version | -v) - echo Makeself version $MS_VERSION - exit 0 - ;; - --pbzip2) - COMPRESS=pbzip2 - shift - ;; - --bzip2) - COMPRESS=bzip2 - shift - ;; - --gzip) - COMPRESS=gzip - shift - ;; - --pigz) - COMPRESS=pigz - shift - ;; - --zstd) - COMPRESS=zstd - shift - ;; - --xz) - COMPRESS=xz - shift - ;; - --lzo) - COMPRESS=lzo - shift - ;; - --lz4) - COMPRESS=lz4 - shift - ;; - --compress) - COMPRESS=compress - shift - ;; - --base64) - COMPRESS=base64 - shift - ;; - --gpg-encrypt) - COMPRESS=gpg - shift - ;; - --gpg-asymmetric-encrypt-sign) - COMPRESS=gpg-asymmetric - shift - ;; - --gpg-extra) - GPG_EXTRA="$2" - shift 2 || { MS_Usage; exit 1; } - ;; - --ssl-encrypt) - ENCRYPT=openssl - shift - ;; - --ssl-passwd) - PASSWD=$2 - shift 2 || { MS_Usage; exit 1; } - ;; - --ssl-pass-src) - PASSWD_SRC=$2 - shift 2 || { MS_Usage; exit 1; } - ;; - --ssl-no-md) - OPENSSL_NO_MD=y - shift - ;; - --nocomp) - COMPRESS=none - shift - ;; - --complevel) - COMPRESS_LEVEL="$2" - shift 2 || { MS_Usage; exit 1; } - ;; - --threads) - THREADS="$2" - shift 2 || { MS_Usage; exit 1; } - ;; - --nochown) - OWNERSHIP=n - shift - ;; - --chown) - OWNERSHIP=y - shift - ;; - --notemp) - KEEP=y - shift - ;; - --copy) - COPY=copy - shift - ;; - --current) - CURRENT=y - KEEP=y - shift - ;; - --tar-format) - TAR_FORMAT="$2" - shift 2 || { MS_Usage; exit 1; } - ;; - --tar-extra) - TAR_EXTRA="$2" - shift 2 || { MS_Usage; exit 1; } - ;; - --untar-extra) - UNTAR_EXTRA="$2" - shift 2 || { MS_Usage; exit 1; } - ;; - --target) - TARGETDIR="$2" - KEEP=y - shift 2 || { MS_Usage; exit 1; } - ;; - --sign) - SIGN=y - GPG_PASSPHRASE="$2" - shift 2 || { MS_Usage; exit 1; } - ;; - --nooverwrite) - NOOVERWRITE=y - shift - ;; - --needroot) - NEED_ROOT=y - shift - ;; - --header) - HEADER="$2" - shift 2 || { MS_Usage; exit 1; } - ;; - --cleanup) - CLEANUP_SCRIPT="$2" - shift 2 || { MS_Usage; exit 1; } - ;; - --license) - # We need to escape all characters having a special meaning in double quotes - LICENSE=$(sed 's/\\/\\\\/g; s/"/\\\"/g; s/`/\\\`/g; s/\$/\\\$/g' "$2") - shift 2 || { MS_Usage; exit 1; } - ;; - --follow) - TAR_ARGS=rvhf - DU_ARGS=-ksL - shift - ;; - --noprogress) - NOPROGRESS=y - shift - ;; - --nox11) - NOX11=y - shift - ;; - --nowait) - NOWAIT=y - shift - ;; - --nomd5) - NOMD5=y - shift - ;; - --sha256) - SHA256=y - shift - ;; - --nocrc) - NOCRC=y - shift - ;; - --append) - APPEND=y - shift - ;; - --lsm) - LSM_CMD="cat \"$2\" >> \"\$archname\"" - shift 2 || { MS_Usage; exit 1; } - ;; - --packaging-date) - DATE="$2" - shift 2 || { MS_Usage; exit 1; } - ;; - --help-header) - HELPHEADER=`sed -e "s/'/'\\\\\''/g" $2` - shift 2 || { MS_Usage; exit 1; } - [ -n "$HELPHEADER" ] && HELPHEADER="$HELPHEADER -" - ;; - --tar-quietly) - TAR_QUIETLY=y - shift - ;; - --keep-umask) - KEEP_UMASK=y - shift - ;; - --export-conf) - EXPORT_CONF=y - shift - ;; - -q | --quiet) - QUIET=y - shift - ;; - -h | --help) - MS_Usage - ;; - -*) - echo Unrecognized flag : "$1" - MS_Usage - ;; - *) - break - ;; - esac -done - -if test $# -lt 1; then - MS_Usage -else - if test -d "$1"; then - archdir="$1" - else - echo "Directory $1 does not exist." >&2 - exit 1 - fi -fi -archname="$2" - -if test "$QUIET" = "y" || test "$TAR_QUIETLY" = "y"; then - if test "$TAR_ARGS" = "rvf"; then - TAR_ARGS="rf" - elif test "$TAR_ARGS" = "rvhf"; then - TAR_ARGS="rhf" - fi -fi - -if test "$APPEND" = y; then - if test $# -lt 2; then - MS_Usage - fi - - # Gather the info from the original archive - OLDENV=`sh "$archname" --dumpconf` - if test $? -ne 0; then - echo "Unable to update archive: $archname" >&2 - exit 1 - else - eval "$OLDENV" - OLDSKIP=`expr $SKIP + 1` - fi -else - if test "$KEEP" = n -a $# = 3; then - echo "ERROR: Making a temporary archive with no embedded command does not make sense!" >&2 - echo >&2 - MS_Usage - fi - # We don't want to create an absolute directory unless a target directory is defined - if test "$CURRENT" = y; then - archdirname="." - elif test x"$TARGETDIR" != x; then - archdirname="$TARGETDIR" - else - archdirname=`basename "$1"` - fi - - if test $# -lt 3; then - MS_Usage - fi - - LABEL="$3" - SCRIPT="$4" - test "x$SCRIPT" = x || shift 1 - shift 3 - SCRIPTARGS="$*" -fi - -if test "$KEEP" = n -a "$CURRENT" = y; then - echo "ERROR: It is A VERY DANGEROUS IDEA to try to combine --notemp and --current." >&2 - exit 1 -fi - -case $COMPRESS in -gzip) - GZIP_CMD="gzip -c$COMPRESS_LEVEL" - GUNZIP_CMD="gzip -cd" - ;; -pigz) - GZIP_CMD="pigz -$COMPRESS_LEVEL" - if test $THREADS -ne $DEFAULT_THREADS; then # Leave as the default if threads not indicated - GZIP_CMD="$GZIP_CMD --processes $THREADS" - fi - GUNZIP_CMD="gzip -cd" - ;; -zstd) - GZIP_CMD="zstd -$COMPRESS_LEVEL" - if test $THREADS -ne $DEFAULT_THREADS; then # Leave as the default if threads not indicated - GZIP_CMD="$GZIP_CMD --threads=$THREADS" - fi - GUNZIP_CMD="zstd -cd" - ;; -pbzip2) - GZIP_CMD="pbzip2 -c$COMPRESS_LEVEL" - if test $THREADS -ne $DEFAULT_THREADS; then # Leave as the default if threads not indicated - GZIP_CMD="$GZIP_CMD -p$THREADS" - fi - GUNZIP_CMD="bzip2 -d" - ;; -bzip2) - GZIP_CMD="bzip2 -$COMPRESS_LEVEL" - GUNZIP_CMD="bzip2 -d" - ;; -xz) - GZIP_CMD="xz -c$COMPRESS_LEVEL" - # Must opt-in by specifying a value since not all versions of xz support threads - if test $THREADS -ne $DEFAULT_THREADS; then - GZIP_CMD="$GZIP_CMD --threads=$THREADS" - fi - GUNZIP_CMD="xz -d" - ;; -lzo) - GZIP_CMD="lzop -c$COMPRESS_LEVEL" - GUNZIP_CMD="lzop -d" - ;; -lz4) - GZIP_CMD="lz4 -c$COMPRESS_LEVEL" - GUNZIP_CMD="lz4 -d" - ;; -base64) - GZIP_CMD="base64" - GUNZIP_CMD="base64 --decode -i -" - ;; -gpg) - GZIP_CMD="gpg $GPG_EXTRA -ac -z$COMPRESS_LEVEL" - GUNZIP_CMD="gpg -d" - ENCRYPT="gpg" - ;; -gpg-asymmetric) - GZIP_CMD="gpg $GPG_EXTRA -z$COMPRESS_LEVEL -es" - GUNZIP_CMD="gpg --yes -d" - ENCRYPT="gpg" - ;; -compress) - GZIP_CMD="compress -fc" - GUNZIP_CMD="(type compress >/dev/null 2>&1 && compress -fcd || gzip -cd)" - ;; -none) - GZIP_CMD="cat" - GUNZIP_CMD="cat" - ;; -esac - -if test x"$ENCRYPT" = x"openssl"; then - if test x"$APPEND" = x"y"; then - echo "Appending to existing archive is not compatible with OpenSSL encryption." >&2 - fi - - ENCRYPT_CMD="openssl enc -aes-256-cbc -salt" - DECRYPT_CMD="openssl enc -aes-256-cbc -d" - - if test x"$OPENSSL_NO_MD" != x"y"; then - ENCRYPT_CMD="$ENCRYPT_CMD -md sha256" - DECRYPT_CMD="$DECRYPT_CMD -md sha256" - fi - - if test -n "$PASSWD_SRC"; then - ENCRYPT_CMD="$ENCRYPT_CMD -pass $PASSWD_SRC" - elif test -n "$PASSWD"; then - ENCRYPT_CMD="$ENCRYPT_CMD -pass pass:$PASSWD" - fi -fi - -tmpfile="${TMPDIR:-/tmp}/mkself$$" - -if test -f "$HEADER"; then - oldarchname="$archname" - archname="$tmpfile" - # Generate a fake header to count its lines - SKIP=0 - . "$HEADER" - SKIP=`cat "$tmpfile" |wc -l` - # Get rid of any spaces - SKIP=`expr $SKIP` - rm -f "$tmpfile" - if test "$QUIET" = "n"; then - echo "Header is $SKIP lines long" >&2 - fi - archname="$oldarchname" -else - echo "Unable to open header file: $HEADER" >&2 - exit 1 -fi - -if test "$QUIET" = "n"; then - echo -fi - -if test "$APPEND" = n; then - if test -f "$archname"; then - echo "WARNING: Overwriting existing file: $archname" >&2 - fi -fi - -USIZE=`du $DU_ARGS "$archdir" | awk '{print $1}'` - -if test "." = "$archdirname"; then - if test "$KEEP" = n; then - archdirname="makeself-$$-`date +%Y%m%d%H%M%S`" - fi -fi - -test -d "$archdir" || { echo "Error: $archdir does not exist."; rm -f "$tmpfile"; exit 1; } -if test "$QUIET" = "n"; then - echo "About to compress $USIZE KB of data..." - echo "Adding files to archive named \"$archname\"..." -fi - -# See if we have GNU tar -TAR=`exec <&- 2>&-; which gtar || command -v gtar || type gtar` -test -x "$TAR" || TAR=tar - -tmparch="${TMPDIR:-/tmp}/mkself$$.tar" -( - if test "$APPEND" = "y"; then - tail -n "+$OLDSKIP" "$archname" | eval "$GUNZIP_CMD" > "$tmparch" - fi - cd "$archdir" - # "Determining if a directory is empty" - # https://www.etalabs.net/sh_tricks.html - find . \ - \( \ - ! -type d \ - -o \ - \( -links 2 -exec sh -c ' - is_empty () ( - cd "$1" - set -- .[!.]* ; test -f "$1" && return 1 - set -- ..?* ; test -f "$1" && return 1 - set -- * ; test -f "$1" && return 1 - return 0 - ) - is_empty "$0"' {} \; \ - \) \ - \) -print \ - | LC_ALL=C sort \ - | sed 's/./\\&/g' \ - | xargs $TAR $TAR_EXTRA --format $TAR_FORMAT -$TAR_ARGS "$tmparch" -) || { - echo "ERROR: failed to create temporary archive: $tmparch" - rm -f "$tmparch" "$tmpfile" - exit 1 -} - -USIZE=`du $DU_ARGS "$tmparch" | awk '{print $1}'` - -eval "$GZIP_CMD" <"$tmparch" >"$tmpfile" || { - echo "ERROR: failed to create temporary file: $tmpfile" - rm -f "$tmparch" "$tmpfile" - exit 1 -} -rm -f "$tmparch" - -if test x"$ENCRYPT" = x"openssl"; then - echo "About to encrypt archive \"$archname\"..." - { eval "$ENCRYPT_CMD -in $tmpfile -out ${tmpfile}.enc" && mv -f ${tmpfile}.enc $tmpfile; } || \ - { echo Aborting: could not encrypt temporary file: "$tmpfile".; rm -f "$tmpfile"; exit 1; } -fi - -fsize=`cat "$tmpfile" | wc -c | tr -d " "` - -# Compute the checksums - -shasum=0000000000000000000000000000000000000000000000000000000000000000 -md5sum=00000000000000000000000000000000 -crcsum=0000000000 - -if test "$NOCRC" = y; then - if test "$QUIET" = "n"; then - echo "skipping crc at user request" - fi -else - crcsum=`CMD_ENV=xpg4 cksum < "$tmpfile" | sed -e 's/ /Z/' -e 's/ /Z/' | cut -dZ -f1` - if test "$QUIET" = "n"; then - echo "CRC: $crcsum" - fi -fi - -if test "$SHA256" = y; then - SHA_PATH=`exec <&- 2>&-; which shasum || command -v shasum || type shasum` - if test -x "$SHA_PATH"; then - shasum=`eval "$SHA_PATH -a 256" < "$tmpfile" | cut -b-64` - else - SHA_PATH=`exec <&- 2>&-; which sha256sum || command -v sha256sum || type sha256sum` - shasum=`eval "$SHA_PATH" < "$tmpfile" | cut -b-64` - fi - if test "$QUIET" = "n"; then - if test -x "$SHA_PATH"; then - echo "SHA256: $shasum" - else - echo "SHA256: none, SHA command not found" - fi - fi -fi -if test "$NOMD5" = y; then - if test "$QUIET" = "n"; then - echo "Skipping md5sum at user request" - fi -else - # Try to locate a MD5 binary - OLD_PATH=$PATH - PATH=${GUESS_MD5_PATH:-"$OLD_PATH:/bin:/usr/bin:/sbin:/usr/local/ssl/bin:/usr/local/bin:/opt/openssl/bin"} - MD5_ARG="" - MD5_PATH=`exec <&- 2>&-; which md5sum || command -v md5sum || type md5sum` - test -x "$MD5_PATH" || MD5_PATH=`exec <&- 2>&-; which md5 || command -v md5 || type md5` - test -x "$MD5_PATH" || MD5_PATH=`exec <&- 2>&-; which digest || command -v digest || type digest` - PATH=$OLD_PATH - if test -x "$MD5_PATH"; then - if test `basename ${MD5_PATH}`x = digestx; then - MD5_ARG="-a md5" - fi - md5sum=`eval "$MD5_PATH $MD5_ARG" < "$tmpfile" | cut -b-32` - if test "$QUIET" = "n"; then - echo "MD5: $md5sum" - fi - else - if test "$QUIET" = "n"; then - echo "MD5: none, MD5 command not found" - fi - fi -fi -if test "$SIGN" = y; then - GPG_PATH=`exec <&- 2>&-; which gpg || command -v gpg || type gpg` - if test -x "$GPG_PATH"; then - SIGNATURE=`$GPG_PATH --pinentry-mode=loopback --batch --yes --passphrase "$GPG_PASSPHRASE" --output - --detach-sig $tmpfile | base64 | tr -d \\\\n` - if test "$QUIET" = "n"; then - echo "Signature: $SIGNATURE" - fi - else - echo "Missing gpg command" >&2 - fi -fi - -totalsize=0 -for size in $fsize; -do - totalsize=`expr $totalsize + $size` -done - -if test "$APPEND" = y; then - mv "$archname" "$archname".bak || exit - - # Prepare entry for new archive - filesizes="$fsize" - CRCsum="$crcsum" - MD5sum="$md5sum" - SHAsum="$shasum" - Signature="$SIGNATURE" - # Generate the header - . "$HEADER" - # Append the new data - cat "$tmpfile" >> "$archname" - - chmod +x "$archname" - rm -f "$archname".bak - if test "$QUIET" = "n"; then - echo "Self-extractable archive \"$archname\" successfully updated." - fi -else - filesizes="$fsize" - CRCsum="$crcsum" - MD5sum="$md5sum" - SHAsum="$shasum" - Signature="$SIGNATURE" - - # Generate the header - . "$HEADER" - - # Append the compressed tar data after the stub - if test "$QUIET" = "n"; then - echo - fi - cat "$tmpfile" >> "$archname" - chmod +x "$archname" - if test "$QUIET" = "n"; then - echo Self-extractable archive \"$archname\" successfully created. - fi -fi -rm -f "$tmpfile" diff --git a/tools/makeself-2.4.5/makeself-header.sh b/tools/makeself-header.sh similarity index 100% rename from tools/makeself-2.4.5/makeself-header.sh rename to tools/makeself-header.sh