diff --git a/bin/acme b/bin/acme index d4211d5..6c2c61f 100755 --- a/bin/acme +++ b/bin/acme @@ -5,15 +5,19 @@ use Auryn\Injector; use Kelunik\AcmeClient\AcmeFactory; use League\CLImate\CLImate; +$logo = << "Setup and register account.", + "issue" => "Issue a new certificate.", + "check" => "Check if a certificate is still valid long enough.", + "revoke" => "Revoke a certificate.", + "version" => "Print version information.", ]; $binary = \Kelunik\AcmeClient\getBinary(); -$help = implode("\n ", array_map(function ($command) use ($binary) { - return "{$binary} {$command}"; -}, $commands)); +$help = implode(PHP_EOL, array_map(function ($command) use ($commands) { + $help = " {$command}\n"; + $help .= " └─ {$commands[$command]}\n"; + return $help; +}, array_keys($commands))); $help = <<Usage: + bin/acme [command] [--args] - Usage: bin/acme command --args - - Available Commands: - {$help} - - Get more help by appending --help to specific commands. +Options: + -h, --help + └─ Print this help message. +Available commands: +{$help} +Get more help by appending --help to specific commands. EOT; $climate = new CLImate; if (!in_array(PHP_SAPI, ["cli", "phpdbg"], true)) { - $climate->error("Please run this script via CLI!"); + $climate->error("Please run this script on the command line!"); exit(1); } if (count($argv) === 1 || in_array($argv[1], ["h", "-h", "help", "--help"], true)) { - $climate->out($help); + $climate->out($logo . $help); exit(0); } -if (!in_array($argv[1], $commands)) { - $climate->br()->error(" Unknown command '{$argv[1]}'. Use --help for a list of available commands."); +if (!in_array($argv[1], array_keys($commands))) { + $climate->error("Unknown command '{$argv[1]}'. Use --help for a list of available commands."); - $suggestion = \Kelunik\AcmeClient\suggestCommand($argv[1], $commands); + $suggestion = \Kelunik\AcmeClient\suggestCommand($argv[1], array_keys($commands)); if ($suggestion) { - $climate->br()->out(" Did you mean '$suggestion'?"); + $climate->br()->out(" Did you mean '$suggestion'?"); } $climate->br(); diff --git a/composer.json b/composer.json index cf8974b..58e9ed5 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "kelunik/acme-client", - "description": "Standalone PHP ACME client.", + "description": "Let's Encrypt / ACME client written in PHP for the CLI.", "keywords": [ "ACME", "letsencrypt", @@ -24,7 +24,8 @@ "require-dev": { "phpunit/phpunit": "^5", "fabpot/php-cs-fixer": "^1.9", - "macfja/phar-builder": "dev-master#a2db582eab26ef7b15144c013408749a79fae361" + "macfja/phar-builder": "dev-master#97bfa5ffb2bb8beb26db2d5575e852dd10593ac3", + "league/event": "^2.1" }, "license": "MIT", "authors": [ @@ -48,8 +49,19 @@ "compression": "GZip", "name": "acme-client.phar", "output-dir": "build", - "include": ["src", "vendor/kelunik/acme/res", "vendor/amphp/socket/var"], - "entry-point": "bin/acme" + "include": ["info", "src", "vendor/kelunik/acme/res", "vendor/amphp/socket/var"], + "entry-point": "bin/acme", + "events": { + "build.before": [ + "mkdir -p info", + "git describe --tags > info/build.version", + "php -r 'echo time();' > info/build.time" + ], + "build.after": [ + "rm -rf info", + "chmod +x build/acme-client.phar" + ] + } } } } diff --git a/composer.lock b/composer.lock index 2b7b29d..5173eb3 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "3bfc6d13e53f6be6788859cbdd68b52a", - "content-hash": "62b8fb8281fc8a7b9925c115301f4707", + "hash": "e1091f4600ce5c211e5c11f89718d7cf", + "content-hash": "55e9166fdb7dfbdbda7ef9144e494cdd", "packages": [ { "name": "amphp/amp", @@ -172,16 +172,16 @@ }, { "name": "amphp/dns", - "version": "v0.8.9", + "version": "v0.8.10", "source": { "type": "git", "url": "https://github.com/amphp/dns.git", - "reference": "78cda5692a80ec0a5b50ee7616d93426961836ec" + "reference": "9f12b2264f74e0cbad67565308f9e106fb6a5bec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/amphp/dns/zipball/78cda5692a80ec0a5b50ee7616d93426961836ec", - "reference": "78cda5692a80ec0a5b50ee7616d93426961836ec", + "url": "https://api.github.com/repos/amphp/dns/zipball/9f12b2264f74e0cbad67565308f9e106fb6a5bec", + "reference": "9f12b2264f74e0cbad67565308f9e106fb6a5bec", "shasum": "" }, "require": { @@ -236,7 +236,7 @@ "dns", "resolve" ], - "time": "2016-03-24 19:17:04" + "time": "2016-03-26 20:35:44" }, { "name": "amphp/file", @@ -1210,18 +1210,68 @@ "description": "A tool to automatically fix PHP code style", "time": "2016-02-26 07:37:29" }, + { + "name": "league/event", + "version": "2.1.2", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/event.git", + "reference": "e4bfc88dbcb60c8d8a2939a71f9813e141bbe4cd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/event/zipball/e4bfc88dbcb60c8d8a2939a71f9813e141bbe4cd", + "reference": "e4bfc88dbcb60c8d8a2939a71f9813e141bbe4cd", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "require-dev": { + "henrikbjorn/phpspec-code-coverage": "~1.0.1", + "phpspec/phpspec": "~2.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Event\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frenky.net" + } + ], + "description": "Event package", + "keywords": [ + "emitter", + "event", + "listener" + ], + "time": "2015-05-21 12:24:47" + }, { "name": "macfja/phar-builder", "version": "dev-master", "source": { "type": "git", "url": "https://github.com/MacFJA/PharBuilder.git", - "reference": "a2db582eab26ef7b15144c013408749a79fae361" + "reference": "97bfa5ffb2bb8beb26db2d5575e852dd10593ac3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/MacFJA/PharBuilder/zipball/9396c639ca105f4c07649fbc15200297e8981764", - "reference": "a2db582eab26ef7b15144c013408749a79fae361", + "url": "https://api.github.com/repos/MacFJA/PharBuilder/zipball/8a2d96e9497418729d00df1e86a1778f8a9f3268", + "reference": "97bfa5ffb2bb8beb26db2d5575e852dd10593ac3", "shasum": "" }, "require": { @@ -1230,6 +1280,11 @@ "symfony/console": "~2.6", "webignition/readable-duration": "~0" }, + "require-dev": { + "phpmd/phpmd": "^2.4", + "sebastian/phpcpd": "^2.0", + "squizlabs/php_codesniffer": "^2.5" + }, "bin": [ "bin/phar-builder", "bin/phar-builder.php" @@ -1261,7 +1316,7 @@ } ], "description": "CLI tool for create phar of your composer based project", - "time": "2016-03-24 00:08:01" + "time": "2016-03-27 12:42:09" }, { "name": "macfja/symfony-console-filechooser", @@ -2346,16 +2401,16 @@ }, { "name": "symfony/console", - "version": "v2.8.3", + "version": "v2.8.4", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "56cc5caf051189720b8de974e4746090aaa10d44" + "reference": "9a5aef5fc0d4eff86853d44202b02be8d5a20154" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/56cc5caf051189720b8de974e4746090aaa10d44", - "reference": "56cc5caf051189720b8de974e4746090aaa10d44", + "url": "https://api.github.com/repos/symfony/console/zipball/9a5aef5fc0d4eff86853d44202b02be8d5a20154", + "reference": "9a5aef5fc0d4eff86853d44202b02be8d5a20154", "shasum": "" }, "require": { @@ -2402,7 +2457,7 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2016-02-28 16:20:50" + "time": "2016-03-17 09:19:04" }, { "name": "symfony/event-dispatcher", @@ -2515,16 +2570,16 @@ }, { "name": "symfony/finder", - "version": "v2.8.3", + "version": "v2.8.4", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "877bb4b16ea573cc8c024e9590888fcf7eb7e0f7" + "reference": "ca24cf2cd4e3826f571e0067e535758e73807aa1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/877bb4b16ea573cc8c024e9590888fcf7eb7e0f7", - "reference": "877bb4b16ea573cc8c024e9590888fcf7eb7e0f7", + "url": "https://api.github.com/repos/symfony/finder/zipball/ca24cf2cd4e3826f571e0067e535758e73807aa1", + "reference": "ca24cf2cd4e3826f571e0067e535758e73807aa1", "shasum": "" }, "require": { @@ -2560,7 +2615,7 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2016-02-22 16:12:45" + "time": "2016-03-10 10:53:53" }, { "name": "symfony/polyfill-mbstring", diff --git a/src/Commands/Version.php b/src/Commands/Version.php new file mode 100644 index 0000000..1ab87c3 --- /dev/null +++ b/src/Commands/Version.php @@ -0,0 +1,76 @@ +climate = $climate; + } + + public function execute(Manager $args) { + $version = $this->getVersion(); + + $buildTime = $this->readFileOr("info/build.time", time()); + $buildDate = date('M jS Y H:i:s T', (int) trim($buildTime)); + + $package = json_decode($this->readFileOr("composer.json", new RuntimeException("No composer.json found."))); + + $this->climate->out("┌ kelunik/acme-client @ {$version} (built: {$buildDate})"); + $this->climate->out(($args->defined("deps") ? "│" : "└") . " " . $this->getDescription($package)); + + if ($args->defined("deps")) { + $lockFile = json_decode($this->readFileOr("composer.lock", new RuntimeException("No composer.lock found."))); + $packages = $lockFile->packages; + + for ($i = 0; $i < count($packages); $i++) { + $link = $i === count($packages) - 1 ? "└──" : "├──"; + $this->climate->out("{$link} {$packages[$i]->name} @ {$packages[$i]->version}"); + + $link = $i === count($packages) - 1 ? " " : "│ "; + $this->climate->out("{$link} " . $this->getDescription($packages[$i])); + } + } + } + + private function getDescription($package) { + return \Kelunik\AcmeClient\ellipsis(isset($package->description) ? $package->description : ""); + } + + private function getVersion() { + if (file_exists(__DIR__ . "/../../.git")) { + $version = `git describe --tags`; + } else { + $version = $this->readFileOr("info/build.version", "-unknown"); + } + + return substr(trim($version), 1); + } + + private function readFileOr($file, $default = "") { + if (file_exists(__DIR__ . "/../../" . $file)) { + return file_get_contents(__DIR__ . "/../../" . $file); + } else { + if ($default instanceof \Exception || $default instanceof \Throwable) { + throw $default; + } + + return $default; + } + } + + public static function getDefinition() { + return [ + "deps" => [ + "longPrefix" => "deps", + "description" => "Show also the bundled dependency versions.", + "noValue" => true, + ], + ]; + } +} \ No newline at end of file diff --git a/src/functions.php b/src/functions.php index cb5f886..ddf450d 100644 --- a/src/functions.php +++ b/src/functions.php @@ -218,4 +218,26 @@ function getBinary() { } return $binary; +} + +/** + * Cuts a text to a certain length and appends an ellipsis if necessary. + * + * @param string $text text to shorten + * @param int $max maximum length + * @param string $append appendix when too long + * @return string shortened string + */ +function ellipsis($text, $max = 70, $append = "…") { + if (strlen($text) <= $max) { + return $text; + } + + $out = substr($text, 0, $max); + + if (strpos($text, " ") === false) { + return $out . $append; + } + + return preg_replace("/\\w+$/", "", $out) . $append; } \ No newline at end of file