* initial commit

This commit is contained in:
David Danyi
2019-10-29 21:45:28 +01:00
commit 2cf81c493e
35 changed files with 3616 additions and 0 deletions

View File

@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace App;
use Interop\Container\ContainerInterface;
use Symfony\Component\Console\Application;
class ApplicationFactory
{
public function __invoke(ContainerInterface $container): Application
{
return new Application('Application console');
}
}

View File

@@ -0,0 +1,82 @@
<?php
declare(strict_types=1);
namespace App\Command;
use App\Downloader\SCP;
use App\Service\Builder;
use Doctrine\Common\Collections\Collection;
use Exception;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Process\Process;
class BuildPackage extends Command
{
use SharedStorage;
const NAME = "standalone:build-package";
/** @var Builder */
private $builder;
public function __construct(Builder $builder, Collection $storage)
{
$this->builder = $builder;
$this->sharedStorage = $storage;
parent::__construct();
}
protected function configure()
{
$this
->setName(self::NAME)
->setDescription('Build packages on remote host')
->addOption("host", null, InputOption::VALUE_REQUIRED, "", false)
->addOption("path", null, InputOption::VALUE_REQUIRED, "", false)
;
}
protected function saveToShared(string $name, $value)
{
$this->sharedStorage->set(sprintf("%s:%s", self::NAME, $name), $value);
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @throws Exception
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$host = $input->getOption("host");
$path = $input->getOption("path");
if (!$host && !$path) {
throw new Exception("Missing arguments");
}
$this->saveToShared("host", $host);
$this->saveToShared("path", $path);
$output->writeln("<info>Building package:</info>");
// $helper = $this->getHelper('process');
// $tableSection = $output->section();
// $totalProgressSection = $output->section();
// $itemProgressSection = $output->section();
// $table = new Table($tableSection);
// $table->setHeaders([
// 'Title',
// 'Pages',
// 'Size',
// ]);
// $table->render();
// $totalProgress = new ProgressBar($totalProgressSection);
// $itemProgress = new ProgressBar($itemProgressSection);
$remotePackage = $this->builder->buildRemotePackage($host, $path);
$this->saveToShared("package", $remotePackage);
$downloader = new SCP();
$downloader->download(sprintf("%s:%s", $host, $remotePackage), ".");
}
}

View File

@@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace App\Command;
use App\Service\Builder;
use Doctrine\Common\Collections\ArrayCollection;
use Interop\Container\ContainerInterface;
class BuildPackageFactory
{
public function __invoke(ContainerInterface $container): BuildPackage
{
/** @var Builder $builder */
$builder = $container->get(Builder::class);
$sharedStorage = $container->get(ArrayCollection::class);
return new BuildPackage($builder, $sharedStorage);
}
}

View File

@@ -0,0 +1,63 @@
<?php
declare(strict_types=1);
namespace App\Command;
use App\Service\ConfigProvider;
use Doctrine\Common\Collections\ArrayCollection;
use Exception;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class ComplexTaskConfig extends Command
{
/** @var ConfigProvider */
private $configProvider;
/** @var ArrayCollection */
private $sharedStorage;
public function __construct(ConfigProvider $configProvider, ArrayCollection $sharedStorage)
{
$this->configProvider = $configProvider;
$this->sharedStorage = $sharedStorage;
parent::__construct();
}
protected function configure()
{
$this->setName('complex:task-config')
->setDescription('Build using task config');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @throws Exception
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$taskConfig = $this->configProvider->getTaskConfig();
$app = $this->getApplication();
foreach ($taskConfig as $task) {
$cmd = $app->find($task['command']);
$args = [
'command' => $task['command']
];
if (isset($task['args']) && is_array($task['args'])) {
$args += $task['args'];
}
if (isset($task['stored-args']) && is_array($task['stored-args'])) {
foreach ($task['stored-args'] as $key => $storedArg) {
$args[$key] = $this->sharedStorage->get($storedArg);
}
}
$cmd->run(new ArrayInput($args), $output);
}
}
}

View File

@@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace App\Command;
use App\Service\ConfigProvider;
use Doctrine\Common\Collections\ArrayCollection;
use Interop\Container\ContainerInterface;
class ComplexTaskConfigFactory
{
public function __invoke(ContainerInterface $container): ComplexTaskConfig
{
/** @var ConfigProvider $configProvider */
$configProvider = $container->get(ConfigProvider::class);
/** @var ArrayCollection $builder */
$builder = $container->get(ArrayCollection::class);
return new ComplexTaskConfig($configProvider, $builder);
}
}

View File

@@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
namespace App\Command;
use App\Service\Builder;
use Doctrine\Common\Collections\Collection;
use Exception;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class GenerateOnboardPackage extends Command
{
use SharedStorage;
const NAME = "standalone:generate-onboard-package";
/** @var Builder */
private $builder;
public function __construct(Builder $builder, Collection $storage)
{
$this->builder = $builder;
$this->sharedStorage = $storage;
parent::__construct();
}
protected function configure()
{
$this
->setName(self::NAME)
->setDescription('Gen onboard package')
->addOption("host", null, InputOption::VALUE_REQUIRED, "", false)
->addOption("package", null, InputOption::VALUE_REQUIRED, "", false)
;
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @throws Exception
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$host = $input->getOption("host");
$package = $input->getOption("package");
if (!$host && !$package) {
throw new Exception("Missing arguments");
}
$this->saveToShared("host", $host);
$this->saveToShared("path", $package);
$output->writeln("<info>Generate onboard package:</info>");
$output->writeln("Got args: " . $input->getOption("host") . $input->getOption("package"));
}
}

View File

@@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace App\Command;
use App\Service\Builder;
use Doctrine\Common\Collections\ArrayCollection;
use Interop\Container\ContainerInterface;
class GenerateOnboardPackageFactory
{
public function __invoke(ContainerInterface $container): GenerateOnboardPackage
{
/** @var Builder $builder */
$builder = $container->get(Builder::class);
$sharedStorage = $container->get(ArrayCollection::class);
return new GenerateOnboardPackage($builder, $sharedStorage);
}
}

View File

@@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace App\Command;
use Doctrine\Common\Collections\Collection;
trait SharedStorage
{
/** @var Collection */
protected $sharedStorage;
protected function saveToShared(string $name, $value)
{
$this->sharedStorage->set(sprintf("%s:%s", self::NAME, $name), $value);
}
}

View File

@@ -0,0 +1,67 @@
<?php
declare(strict_types=1);
namespace App;
use App\Command;
use Doctrine\Common\Collections\ArrayCollection;
/**
* The configuration provider for the App module
*
* @see https://docs.zendframework.com/zend-component-installer/
*/
class ConfigProvider
{
/**
* Returns the configuration array
*
* To add a bit of a structure, each section is defined in a separate
* method which returns an array with its configuration.
*
*/
public function __invoke() : array
{
return [
'dependencies' => $this->getDependencies(),
'console' => $this->getConsole(),
];
}
/**
* Returns the container dependencies
*/
public function getDependencies() : array
{
return [
'invokables' => [
ArrayCollection::class,
],
'factories' => [
Service\Builder::class => Service\BuilderFactory::class,
Service\ConfigProvider::class => Service\ConfigProviderFactory::class,
Command\BuildPackage::class => Command\BuildPackageFactory::class,
Command\ComplexTaskConfig::class => Command\ComplexTaskConfigFactory::class,
Command\GenerateOnboardPackage::class => Command\GenerateOnboardPackageFactory::class,
],
];
}
/**
* Returns the templates configuration
*/
public function getConsole() : array
{
return [
'commands' => [
Command\ComplexTaskConfig::class,
],
'lazy_commands' => [
Command\BuildPackage::NAME => Command\BuildPackage::class,
Command\GenerateOnboardPackage::NAME => Command\GenerateOnboardPackage::class,
],
];
}
}

View File

@@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace App\Downloader;
interface DownloaderInterface {
public function download(string $remote, string $local): string;
}

View File

@@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace App\Downloader;
use Symfony\Component\Process\Process;
class SCP implements DownloaderInterface
{
public function download(string $remote, string $local): string
{
$process = new Process([
'scp',
$remote,
$local
]);
$process->run();
return $process->getOutput();
}
}

19
src/App/Runner/Local.php Normal file
View File

@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace App\Runner;
use Symfony\Component\Process\Process;
class Local implements RunnerInterface
{
public function execute($command): string
{
$process = new Process([
$command
]);
$process->run();
return $process->getOutput();
}
}

View File

@@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace App\Runner;
interface RunnerInterface {
public function execute($command): string;
}

31
src/App/Runner/SSH.php Normal file
View File

@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace App\Runner;
use Symfony\Component\Process\Process;
class SSH implements RunnerInterface
{
private $hostname;
public function __construct($hostname)
{
$this->hostname = $hostname;
}
public function execute($command): string
{
$process = new Process([
'ssh',
$this->hostname,
$command
]);
$process->run();
if (!$process->isSuccessful()) {
throw new \Exception("Command failed with: " . $process->getErrorOutput());
}
return $process->getOutput();
}
}

View File

@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace App\Service;
use App\Runner\SSH;
use Exception;
class Builder
{
const PACKAGE_BUILDER_SCRIPT = "prepare_package.sh";
/**
* @param $host
* @param $path
* @return string Generated filename on remote host
* @throws Exception
*/
public function buildRemotePackage($host, $path): string
{
$runner = new SSH($host);
$out = $runner->execute(sprintf("%s/%s", $path, self::PACKAGE_BUILDER_SCRIPT));
$lines = explode("\n", trim($out));
return array_pop($lines);
}
}

View File

@@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace App\Service;
use Interop\Container\ContainerInterface;
class BuilderFactory
{
public function __invoke(ContainerInterface $container): Builder
{
return new Builder();
}
}

View File

@@ -0,0 +1,65 @@
<?php
declare(strict_types=1);
namespace App\Service;
use InvalidArgumentException;
use Symfony\Component\Yaml\Yaml;
class ConfigProvider
{
const CONFIG_DIR = "automatique";
private $taskFile = "automatique.task.yaml";
private $envFile = "automatique.env.yaml";
private $loadOrder = [];
public function __construct(?string $taskOverride, ?string $envOverride)
{
if ($taskOverride) {
$this->taskFile = $taskOverride;
}
if ($envOverride) {
$this->envFile = $envOverride;
}
$this->loadOrder[] = ".";
$userCfgDir = sprintf("%s/.config/%s", $_SERVER['HOME'], self::CONFIG_DIR);
if (file_exists($userCfgDir)) {
$this->loadOrder[] = $userCfgDir;
}
$globalCfgDir = sprintf("/etc/%s", self::CONFIG_DIR);
if (file_exists($globalCfgDir)) {
$this->loadOrder[] = $globalCfgDir;
}
}
private function findConfigFile($filename): string
{
foreach ($this->loadOrder as $loadDir) {
$configFile = "${loadDir}/${filename}";
if (file_exists($configFile)) {
return $configFile;
}
}
throw new InvalidArgumentException("Couldn't locate $filename");
}
private function readConfigFile($configFile): ?iterable
{
$configFile = $this->findConfigFile($configFile);
return Yaml::parseFile($configFile);
}
public function getEnvironmentConfig(): ?iterable
{
return $this->readConfigFile($this->envFile);
}
public function getTaskConfig(): ?iterable
{
return $this->readConfigFile($this->taskFile);
}
}

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace App\Service;
use Interop\Container\ContainerInterface;
use Symfony\Component\Console\Input\ArgvInput;
class ConfigProviderFactory
{
public function __invoke(ContainerInterface $container): ConfigProvider
{
$taskFile = null;
$envFile = null;
$args = new ArgvInput();
if ($args->hasOption("env-file")) {
$envFile = $args->getOption("env-file");
}
if ($args->hasOption("task-file")) {
$taskFile = $args->getOption("task-file");
}
return new ConfigProvider($taskFile, $envFile);
}
}