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:
parent
8c6f7c1e93
commit
8cc1eac7a4
44
.spark/build/package.sh
Executable file
44
.spark/build/package.sh
Executable file
@ -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/^/ │ /"
|
||||||
|
|
@ -8,5 +8,5 @@ if [ -z "$VERSION" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo -e "<?php define(\"APP_VERSION\", \"$VERSION\");" > src/version
|
echo -e "<?php define(\"APP_VERSION\", \"$VERSION\");" > src/version
|
||||||
echo "[Version: $VERSION]"
|
echo -e "* Version: \e[1m$VERSION\e[0m"
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -1,11 +1,18 @@
|
|||||||
{
|
{
|
||||||
"preload": [
|
"preload": [
|
||||||
".spark/plugins/*"
|
".spark/plugins/*",
|
||||||
|
".spark/local/*"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"version": [
|
||||||
|
".spark/build/update-version.sh"
|
||||||
|
],
|
||||||
|
"package": [
|
||||||
|
".spark/build/package.sh"
|
||||||
|
],
|
||||||
"build": [
|
"build": [
|
||||||
".spark/update-version.sh",
|
"@version",
|
||||||
".spark/package.sh"
|
"@package"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
14
README.md
14
README.md
@ -81,7 +81,7 @@ The advantage of writing your extensions as plugins:
|
|||||||
Using scripts is the simplest way to leverage Spark:
|
Using scripts is the simplest way to leverage Spark:
|
||||||
|
|
||||||
*spark.json*
|
*spark.json*
|
||||||
```
|
```json
|
||||||
{
|
{
|
||||||
...
|
...
|
||||||
"scripts": {
|
"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
|
- Call on other scripts by prepending `@` to the script name.
|
||||||
resources, resource types and plugins.
|
- `.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*
|
- `.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
|
||||||
|
|
||||||
Resources are wrappers around database connections and such, providing a cleaner
|
Resources are wrappers around database connections and such, providing a cleaner
|
||||||
interface to its innards.
|
interface to its innards.
|
||||||
|
|
||||||
Resources are generally registered by plugins.
|
Resources are generally registered by plugins or local scripts.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Changelog
|
# Versions
|
||||||
|
|
||||||
## 0.1.0
|
## 0.1.0
|
||||||
|
|
||||||
@ -8,3 +8,6 @@
|
|||||||
- Spark will automatically chdir to the project root when loading the environment.
|
- 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.
|
This makes sure scripts and plugins etc all start at a known location.
|
||||||
- Added makeself and pharlite in `tools`.
|
- 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).
|
@ -36,6 +36,9 @@
|
|||||||
"symfony/process": "^6.0",
|
"symfony/process": "^6.0",
|
||||||
"psr/log": "^3.0",
|
"psr/log": "^3.0",
|
||||||
"symfony/var-dumper": "^6.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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
26
phpunit.xml
Normal file
26
phpunit.xml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.5/phpunit.xsd"
|
||||||
|
bootstrap="vendor/autoload.php"
|
||||||
|
cacheResultFile=".phpunit.cache/test-results"
|
||||||
|
executionOrder="depends,defects"
|
||||||
|
forceCoversAnnotation="true"
|
||||||
|
beStrictAboutCoversAnnotation="true"
|
||||||
|
beStrictAboutOutputDuringTests="true"
|
||||||
|
beStrictAboutTodoAnnotatedTests="true"
|
||||||
|
failOnRisky="true"
|
||||||
|
failOnWarning="true"
|
||||||
|
verbose="true">
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="default">
|
||||||
|
<directory suffix="Test.php">tests</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
|
||||||
|
<coverage cacheDirectory=".phpunit.cache/code-coverage"
|
||||||
|
processUncoveredFiles="true">
|
||||||
|
<include>
|
||||||
|
<directory suffix=".php">src</directory>
|
||||||
|
</include>
|
||||||
|
</coverage>
|
||||||
|
</phpunit>
|
84
plugins/com.noccy.apiclient/Api/Catalog.php
Normal file
84
plugins/com.noccy.apiclient/Api/Catalog.php
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
<?php // "name":"Call on web APIs", "author":"Noccy"
|
||||||
|
|
||||||
|
namespace SparkPlug\Com\Noccy\ApiClient\Api;
|
||||||
|
|
||||||
|
use JsonSerializable;
|
||||||
|
|
||||||
|
class Catalog implements JsonSerializable
|
||||||
|
{
|
||||||
|
private array $properties = [];
|
||||||
|
|
||||||
|
private array $methods = [];
|
||||||
|
|
||||||
|
private ?string $name;
|
||||||
|
|
||||||
|
private ?string $info;
|
||||||
|
|
||||||
|
public function __construct(array $catalog=[])
|
||||||
|
{
|
||||||
|
$catalog = $catalog['catalog']??[];
|
||||||
|
$this->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,
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
36
plugins/com.noccy.apiclient/Api/Method.php
Normal file
36
plugins/com.noccy.apiclient/Api/Method.php
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?php // "name":"Call on web APIs", "author":"Noccy"
|
||||||
|
|
||||||
|
namespace SparkPlug\Com\Noccy\ApiClient\Api;
|
||||||
|
|
||||||
|
use JsonSerializable;
|
||||||
|
|
||||||
|
class Method implements JsonSerializable
|
||||||
|
{
|
||||||
|
private array $properties = [];
|
||||||
|
|
||||||
|
private ?string $info;
|
||||||
|
|
||||||
|
public function __construct(array $method)
|
||||||
|
{
|
||||||
|
$this->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,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
13
plugins/com.noccy.apiclient/Api/Profile.php
Normal file
13
plugins/com.noccy.apiclient/Api/Profile.php
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?php // "name":"Call on web APIs", "author":"Noccy"
|
||||||
|
|
||||||
|
namespace SparkPlug\Com\Noccy\ApiClient\Api;
|
||||||
|
|
||||||
|
class Profile
|
||||||
|
{
|
||||||
|
private array $properties = [];
|
||||||
|
|
||||||
|
public function getProperties(): array
|
||||||
|
{
|
||||||
|
return $this->properties;
|
||||||
|
}
|
||||||
|
}
|
91
plugins/com.noccy.apiclient/Commands/ApiCatalogCommand.php
Normal file
91
plugins/com.noccy.apiclient/Commands/ApiCatalogCommand.php
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SparkPlug\Com\Noccy\ApiClient\Commands;
|
||||||
|
|
||||||
|
use Spark\Commands\Command;
|
||||||
|
use SparkPlug;
|
||||||
|
use SparkPlug\Com\Noccy\ApiClient\Api\Catalog;
|
||||||
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
|
class ApiCatalogCommand extends Command
|
||||||
|
{
|
||||||
|
protected function configure()
|
||||||
|
{
|
||||||
|
$this->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("<error>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("<info>Created new catalog {$create}</>");
|
||||||
|
return Command::SUCCESS;
|
||||||
|
}
|
||||||
|
if ($remove = $input->getOption("remove")) {
|
||||||
|
if (!file_exists($dest."/".$remove.".json")) {
|
||||||
|
$output->writeln("<error>Catalog {$remove} does not exist!</>");
|
||||||
|
return Command::FAILURE;
|
||||||
|
}
|
||||||
|
unlink($dest."/".$remove.".json");
|
||||||
|
$output->writeln("<info>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("<error>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("<info>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} <options=bold>{$catalog}</>: <fg=gray>{$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}: <info>{$m->getInfo()}</>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Command::SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
35
plugins/com.noccy.apiclient/Commands/ApiLogsCommand.php
Normal file
35
plugins/com.noccy.apiclient/Commands/ApiLogsCommand.php
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SparkPlug\Com\Noccy\ApiClient\Commands;
|
||||||
|
|
||||||
|
use Spark\Commands\Command;
|
||||||
|
use SparkPlug;
|
||||||
|
use SparkPlug\Com\Noccy\ApiClient\Api\Method;
|
||||||
|
use SparkPlug\Com\Noccy\ApiClient\ApiClientPlugin;
|
||||||
|
use SparkPlug\Com\Noccy\ApiClient\Request\RequestBuilder;
|
||||||
|
use Symfony\Component\Console\Helper\Table;
|
||||||
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
|
class ApiLogsCommand extends Command
|
||||||
|
{
|
||||||
|
protected function configure()
|
||||||
|
{
|
||||||
|
$this->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;
|
||||||
|
}
|
||||||
|
}
|
22
plugins/com.noccy.apiclient/Commands/ApiProfileCommand.php
Normal file
22
plugins/com.noccy.apiclient/Commands/ApiProfileCommand.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SparkPlug\Com\Noccy\ApiClient\Commands;
|
||||||
|
|
||||||
|
use Spark\Commands\Command;
|
||||||
|
use SparkPlug;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
|
class ApiProfileCommand extends Command
|
||||||
|
{
|
||||||
|
protected function configure()
|
||||||
|
{
|
||||||
|
$this->setName("api:profile")
|
||||||
|
->setDescription("Manage API profiles");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
|
{
|
||||||
|
return Command::SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
134
plugins/com.noccy.apiclient/Commands/ApiRequestCommand.php
Normal file
134
plugins/com.noccy.apiclient/Commands/ApiRequestCommand.php
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SparkPlug\Com\Noccy\ApiClient\Commands;
|
||||||
|
|
||||||
|
use Spark\Commands\Command;
|
||||||
|
use SparkPlug;
|
||||||
|
use SparkPlug\Com\Noccy\ApiClient\Api\Method;
|
||||||
|
use SparkPlug\Com\Noccy\ApiClient\ApiClientPlugin;
|
||||||
|
use SparkPlug\Com\Noccy\ApiClient\Request\RequestBuilder;
|
||||||
|
use Symfony\Component\Console\Helper\Table;
|
||||||
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
|
class ApiRequestCommand extends Command
|
||||||
|
{
|
||||||
|
protected function configure()
|
||||||
|
{
|
||||||
|
$this->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("<error>No such catalog {$catalog}</>");
|
||||||
|
// return Command::FAILURE;
|
||||||
|
// }
|
||||||
|
$methodObj = $catalogObj->getMethod($method);
|
||||||
|
// if (!$methodObj) {
|
||||||
|
// $output->writeln("<error>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("<error>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("<info>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;
|
||||||
|
}
|
||||||
|
}
|
73
plugins/com.noccy.apiclient/README.md
Normal file
73
plugins/com.noccy.apiclient/README.md
Normal file
@ -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"
|
||||||
|
```
|
91
plugins/com.noccy.apiclient/Request/HttpRequest.php
Normal file
91
plugins/com.noccy.apiclient/Request/HttpRequest.php
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SparkPlug\Com\Noccy\ApiClient\Request;
|
||||||
|
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use GuzzleHttp\Psr7\Response;
|
||||||
|
|
||||||
|
class HttpRequest extends Request
|
||||||
|
{
|
||||||
|
|
||||||
|
private string $method = 'GET';
|
||||||
|
|
||||||
|
private string $version = '1.1';
|
||||||
|
|
||||||
|
private ?string $url = null;
|
||||||
|
|
||||||
|
private array $query = [];
|
||||||
|
|
||||||
|
private array $headers = [];
|
||||||
|
|
||||||
|
public function __construct(array $props)
|
||||||
|
{
|
||||||
|
foreach ($props as $prop=>$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("<options=bold>%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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
26
plugins/com.noccy.apiclient/Request/JsonRpcRequest.php
Normal file
26
plugins/com.noccy.apiclient/Request/JsonRpcRequest.php
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SparkPlug\Com\Noccy\ApiClient\Request;
|
||||||
|
|
||||||
|
use GuzzleHttp\Psr7\Response;
|
||||||
|
|
||||||
|
class JsonRpcRequest extends Request
|
||||||
|
{
|
||||||
|
|
||||||
|
public function getInfo(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function send(): ?Response
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHeaders(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
16
plugins/com.noccy.apiclient/Request/Request.php
Normal file
16
plugins/com.noccy.apiclient/Request/Request.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SparkPlug\Com\Noccy\ApiClient\Request;
|
||||||
|
|
||||||
|
use GuzzleHttp\Psr7\Response;
|
||||||
|
|
||||||
|
abstract class Request
|
||||||
|
{
|
||||||
|
|
||||||
|
abstract public function send(): ?Response;
|
||||||
|
|
||||||
|
abstract public function getInfo(): array;
|
||||||
|
|
||||||
|
abstract public function getHeaders(): array;
|
||||||
|
|
||||||
|
}
|
100
plugins/com.noccy.apiclient/Request/RequestBuilder.php
Normal file
100
plugins/com.noccy.apiclient/Request/RequestBuilder.php
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SparkPlug\Com\Noccy\ApiClient\Request;
|
||||||
|
|
||||||
|
use SparkPlug\Com\Noccy\ApiClient\Api\Catalog;
|
||||||
|
use SparkPlug\Com\Noccy\ApiClient\Api\Method;
|
||||||
|
use SparkPlug\Com\Noccy\ApiClient\Api\Profile;
|
||||||
|
|
||||||
|
class RequestBuilder
|
||||||
|
{
|
||||||
|
public static $Protocols = [
|
||||||
|
'http' => 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;
|
||||||
|
}
|
||||||
|
}
|
26
plugins/com.noccy.apiclient/Request/WebsocketRequest.php
Normal file
26
plugins/com.noccy.apiclient/Request/WebsocketRequest.php
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SparkPlug\Com\Noccy\ApiClient\Request;
|
||||||
|
|
||||||
|
use GuzzleHttp\Psr7\Response;
|
||||||
|
|
||||||
|
class WebsocketRequest extends Request
|
||||||
|
{
|
||||||
|
|
||||||
|
public function getInfo(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function send(): ?Response
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHeaders(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
109
plugins/com.noccy.apiclient/sparkplug.php
Normal file
109
plugins/com.noccy.apiclient/sparkplug.php
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
<?php // "name":"Call on web APIs", "author":"Noccy"
|
||||||
|
|
||||||
|
namespace SparkPlug\Com\Noccy\ApiClient;
|
||||||
|
|
||||||
|
use SparkPlug;
|
||||||
|
|
||||||
|
class ApiClientPlugin extends SparkPlug
|
||||||
|
{
|
||||||
|
private array $catalogs = [];
|
||||||
|
|
||||||
|
private array $profiles = [];
|
||||||
|
|
||||||
|
public function load()
|
||||||
|
{
|
||||||
|
register_command(new Commands\ApiCatalogCommand());
|
||||||
|
register_command(new Commands\ApiRequestCommand());
|
||||||
|
register_command(new Commands\ApiProfileCommand());
|
||||||
|
register_command(new Commands\ApiLogsCommand());
|
||||||
|
}
|
||||||
|
|
||||||
|
private function loadCatalogs()
|
||||||
|
{
|
||||||
|
$env = get_environment();
|
||||||
|
$catalogDir = $env->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);
|
@ -65,7 +65,7 @@ class DockerDbExportCommand extends Command
|
|||||||
$cmd = sprintf("mysqldump -u%s -p%s %s", $dbuser, $dbpass, $database);
|
$cmd = sprintf("mysqldump -u%s -p%s %s", $dbuser, $dbpass, $database);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
$this->exportFromService($service, $cmd, $output);
|
$this->exportFromService($service, $cmd, $output, $input->getOption("output"));
|
||||||
} elseif ($dsn) {
|
} elseif ($dsn) {
|
||||||
$url = parse_url($dsn);
|
$url = parse_url($dsn);
|
||||||
if (empty($url)) {
|
if (empty($url)) {
|
||||||
@ -77,9 +77,13 @@ class DockerDbExportCommand extends Command
|
|||||||
return Command::SUCCESS;
|
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);
|
$cmd = sprintf("docker-compose exec -T %s %s", $service, $command);
|
||||||
|
if ($outfile) {
|
||||||
|
$cmd .= " > ".escapeshellarg($outfile);
|
||||||
|
}
|
||||||
|
$output->writeln(sprintf("→ <info>%s</>", $cmd));
|
||||||
passthru($cmd);
|
passthru($cmd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,8 +70,8 @@ class PluginsCommand extends Command
|
|||||||
$info = object();
|
$info = object();
|
||||||
}
|
}
|
||||||
$installed = in_array(basename($plugin), $localPluginList);
|
$installed = in_array(basename($plugin), $localPluginList);
|
||||||
$badge = ($installed)?"<fg=green>\u{2714}</>":"<fg=gray>\u{27f3}</>";
|
$badge = ($installed)?"<fg=green>\u{2714}</>":"<fg=gray>\u{25cc}</>";
|
||||||
$output->writeln(sprintf(" %s <fg=#0ff>%-20s</> %s", $badge, basename($plugin), $info->name??null));
|
$output->writeln(sprintf(" %s <fg=%s>%-20s</> %s", $badge, ($installed?"#0ff":"#088"), basename($plugin), $info->name??null));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Command::SUCCESS;
|
return Command::SUCCESS;
|
||||||
|
@ -34,7 +34,9 @@ class RunCommand extends Command
|
|||||||
}
|
}
|
||||||
return Command::SUCCESS;
|
return Command::SUCCESS;
|
||||||
} elseif ($script = $input->getArgument('script')) {
|
} 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;
|
return Command::SUCCESS;
|
||||||
|
@ -8,6 +8,7 @@ use Spark\SparkApplication;
|
|||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||||
|
use Symfony\Component\Dotenv\Dotenv;
|
||||||
|
|
||||||
class Environment
|
class Environment
|
||||||
{
|
{
|
||||||
@ -36,12 +37,23 @@ class Environment
|
|||||||
return array_keys($this->config['scripts']??[]);
|
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)
|
public function runScript(string $name, array $args, InputInterface $input, OutputInterface $output)
|
||||||
{
|
{
|
||||||
$script = $this->config['scripts'][$name]??$name;
|
$script = $this->config['scripts'][$name]??$name;
|
||||||
|
|
||||||
if (is_string($script)) {
|
if (is_string($script)) {
|
||||||
$this->execScript($script, $args);
|
$this->execScript($script, $args, $output);
|
||||||
} elseif (is_array($script)) {
|
} elseif (is_array($script)) {
|
||||||
foreach ($script as $row) {
|
foreach ($script as $row) {
|
||||||
$a = str_getcsv($row, ' ', "'");
|
$a = str_getcsv($row, ' ', "'");
|
||||||
@ -50,13 +62,13 @@ class Environment
|
|||||||
$c = ($this->config['scripts'][substr($c,1)])??$c;
|
$c = ($this->config['scripts'][substr($c,1)])??$c;
|
||||||
$this->runScript($c, $a, $input, $output);
|
$this->runScript($c, $a, $input, $output);
|
||||||
} else {
|
} 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
|
// call script directly
|
||||||
if (str_ends_with($script, '.php')) {
|
if (str_ends_with($script, '.php')) {
|
||||||
@ -74,6 +86,7 @@ class Environment
|
|||||||
passthru($script);
|
passthru($script);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
public function loadEnvironment()
|
public function loadEnvironment()
|
||||||
{
|
{
|
||||||
@ -84,9 +97,24 @@ class Environment
|
|||||||
if (!array_key_exists('project_dir', $this->config)) {
|
if (!array_key_exists('project_dir', $this->config)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
chdir($this->config['project_dir']);
|
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...");
|
// $this->logger->info("Loading environment...");
|
||||||
$preloads = [];
|
$preloads = [];
|
||||||
$root = $this->config['project_dir'];
|
$root = $this->config['project_dir'];
|
||||||
@ -102,7 +130,7 @@ class Environment
|
|||||||
if (!str_starts_with($item, "/")) {
|
if (!str_starts_with($item, "/")) {
|
||||||
$item = $this->getProjectDirectory() . "/" . $item;
|
$item = $this->getProjectDirectory() . "/" . $item;
|
||||||
}
|
}
|
||||||
if (is_file($item)) {
|
if (is_file($item) && fnmatch("*.php", $item)) {
|
||||||
// $this->logger->debug("Preloading file {$item}");
|
// $this->logger->debug("Preloading file {$item}");
|
||||||
try {
|
try {
|
||||||
include_once($item);
|
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()}");
|
//$this->logger->error("Error preloading plugin {$item}: {$t->getMessage()} in {$t->getFile()} on line {$t->getLine()}");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fprintf(STDERR, "warning: Could not preload %s\n", $item);
|
// fprintf(STDERR, "warning: Could not preload %s\n", $item);
|
||||||
//$this->logger->warning("Could not preload {$item}");
|
// $this->logger->warning("Could not preload {$item}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,6 +191,11 @@ class Environment
|
|||||||
return $env;
|
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()
|
public function __construct()
|
||||||
{
|
{
|
||||||
parent::__construct("Spark", APP_VERSION);
|
parent::__construct("Spark\u{26a1}", APP_VERSION);
|
||||||
self::$instance = $this;
|
self::$instance = $this;
|
||||||
|
|
||||||
$this->resourceManager = new ResourceManager();
|
$this->resourceManager = new ResourceManager();
|
||||||
@ -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());
|
||||||
|
|
||||||
|
$this->get("list")->setHidden(true);
|
||||||
|
$this->get("completion")->setHidden(true);
|
||||||
|
$this->get("help")->setHidden(true);
|
||||||
|
|
||||||
if (getenv("SPARK_PLUGINS")) {
|
if (getenv("SPARK_PLUGINS")) {
|
||||||
$this->add(new Commands\PluginsCommand());
|
$this->add(new Commands\PluginsCommand());
|
||||||
}
|
}
|
||||||
|
30
src/install
30
src/install
@ -1,5 +1,10 @@
|
|||||||
<?php
|
<?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) {
|
function askConfirm(string $prompt, bool $default) {
|
||||||
|
|
||||||
$pstr = sprintf("%s [%s]? ", $prompt, $default?"Y/n":"y/N");
|
$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");
|
$destination = askString("Installation directory", getenv("HOME")."/opt/spark");
|
||||||
$binaries = askString("Path for executables", getenv("HOME")."/bin");
|
$binaries = askString("Path for executables", getenv("HOME")."/bin");
|
||||||
@ -57,14 +68,17 @@ passthru("cp -R plugins/* ".escapeshellarg($destination."/plugins/"));
|
|||||||
|
|
||||||
if ($doPlugins) {
|
if ($doPlugins) {
|
||||||
$file = sprintf("export SPARK_PLUGINS=\"%s/plugins\"\n", $destination);
|
$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 = file_get_contents(getenv("HOME")."/.bashrc");
|
||||||
$file .= "\nsource ~/.profile_spark\n";
|
if (!str_contains($file, ".bashrc_spark")) {
|
||||||
file_put_contents(getenv("HOME")."/.profile.new", $file);
|
$file .= "\nsource ~/.bashrc_spark\n";
|
||||||
rename(getenv("HOME")."/.profile", getenv("HOME")."/.profile.bak");
|
file_put_contents(getenv("HOME")."/.bashrc.new", $file);
|
||||||
rename(getenv("HOME")."/.profile.new", getenv("HOME")."/.profile");
|
rename(getenv("HOME")."/.bashrc", getenv("HOME")."/.bashrc.bak");
|
||||||
printf("Updated \e[3m.profile\e[0m.\n");
|
rename(getenv("HOME")."/.bashrc.new", getenv("HOME")."/.bashrc");
|
||||||
|
printf("Updated \e[3m.bashrc\e[0m.\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ($doAliases) {
|
if ($doAliases) {
|
||||||
$file = file_get_contents(getenv("HOME")."/.bash_aliases") . "\n";
|
$file = file_get_contents(getenv("HOME")."/.bash_aliases") . "\n";
|
||||||
|
30
tests/Environment/ScriptRunnerTest.php
Normal file
30
tests/Environment/ScriptRunnerTest.php
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Spark\Environment;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ScriptRunnerTest extends \PhpUnit\Framework\TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @dataProvider stringExpansionData
|
||||||
|
* @covers ScriptRunner::expandString
|
||||||
|
*/
|
||||||
|
public function testStringExpansion($source, $expect)
|
||||||
|
{
|
||||||
|
$runner = new ScriptRunner();
|
||||||
|
$expanded = $runner->expandString($source);
|
||||||
|
return $this->assertEquals($expect, $expanded);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stringExpansionData()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[ 'Hello World!', 'Hello World!' ],
|
||||||
|
[ '${testenv}', '' ],
|
||||||
|
[ '${PATH}', getenv("PATH") ],
|
||||||
|
[ 'Greetings ${USER}', 'Greetings '.getenv("USER") ],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1 +0,0 @@
|
|||||||
makeself-2.4.5/makeself.sh
|
|
822
tools/makeself
Executable file
822
tools/makeself
Executable file
@ -0,0 +1,822 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# Makeself version 2.4.x
|
||||||
|
# by Stephane Peter <megastep@megastep.org>
|
||||||
|
#
|
||||||
|
# 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 <megastep@megastep.org>
|
||||||
|
#
|
||||||
|
# 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"
|
@ -1,822 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
#
|
|
||||||
# Makeself version 2.4.x
|
|
||||||
# by Stephane Peter <megastep@megastep.org>
|
|
||||||
#
|
|
||||||
# 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 <megastep@megastep.org>
|
|
||||||
#
|
|
||||||
# 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"
|
|
Loading…
Reference in New Issue
Block a user