From 3582e5142f87c228cad3f8a5a7691029651d964a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20D=C3=A4hne?= Date: Fri, 10 Nov 2023 13:36:08 +0100 Subject: [PATCH 1/4] FEATURE: allow to skip the post-install prunner binary download --- Classes/Composer/InstallerScripts.php | 12 ++++++++++++ README.md | 14 ++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/Classes/Composer/InstallerScripts.php b/Classes/Composer/InstallerScripts.php index 79c047c..e1c08bd 100644 --- a/Classes/Composer/InstallerScripts.php +++ b/Classes/Composer/InstallerScripts.php @@ -45,6 +45,14 @@ class InstallerScripts const DEFAULT_VERSION_TO_INSTALL = '1.0.1'; + /** + * Downloads the prunner binaries from https://github.com/Flowpack/prunner to ./prunner. + * + * You can pin the prunner version in the composer.json#extra.prunner-version field. + * You can also set it to 'skip-download' to skip the download. + * + * @return void + */ public static function postUpdateAndInstall() { $platform = php_uname('s'); // stuff like Darwin etc @@ -58,6 +66,10 @@ public static function postUpdateAndInstall() $version = $extra['prunner-version']; $versionMessage = ' (OVERRIDDEN in composer.json)'; } + if ($version === 'skip-download') { + echo '> Not downloading prunner (due to "skip-download" instruction in composer.json)'; + return; + } $baseDirectory = 'prunner'; $platformSpecificTargetDirectory = $baseDirectory . '/' . $platform . '_' . $architecture; diff --git a/README.md b/README.md index e721038..1897794 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,20 @@ will be downloaded. However, it is possible to override this via `extra.prunner- } ``` +## Skip the Prunner binary download + +In case you want to install Prunner manually, +you can skip the download of the Prunner entirely +by setting `extra.prunner-version` in the root `composer.json` to `"skip-download"`. + +```json +{ + "extra": { + "prunner-version": "skip-download" + } +} +``` + ## Building the UI package In [prunner-ui](https://github.com/Flowpack/prunner-ui), run `yarn build` From aec5f1d1ff0824ff2bfe86e322d66b914d5a1ec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20D=C3=A4hne?= Date: Fri, 10 Nov 2023 13:54:11 +0100 Subject: [PATCH 2/4] FEATURE: support alternative names for the prunner config file --- Classes/Controller/ProxyController.php | 33 +++++++++++++++++++++--- Classes/PrunnerApiService.php | 35 ++++++++++++++++++++------ Configuration/Settings.yaml | 3 +++ 3 files changed, 61 insertions(+), 10 deletions(-) diff --git a/Classes/Controller/ProxyController.php b/Classes/Controller/ProxyController.php index 5851587..9aaed6d 100644 --- a/Classes/Controller/ProxyController.php +++ b/Classes/Controller/ProxyController.php @@ -22,6 +22,12 @@ class ProxyController extends \Neos\Flow\Mvc\Controller\ActionController */ protected $directory; + /** + * @Flow\InjectConfiguration(path="configFile") + * @var string + */ + protected $configFile; + /** * @Flow\InjectConfiguration(path="jwtSecret") * @var string @@ -56,9 +62,8 @@ public function indexAction(string $path, string $subpath) } else { try { // Try to parse prunner config to get JWT secret - $config = Yaml::parseFile($this->directory . '/.prunner.yml'); - $jwtSecret = $config['jwt_secret']; - } catch (ParseException $e) { + $jwtSecret = $this->loadJwtSecretFromConfigFile(); + } catch (\RuntimeException $e) { $this->response->setContentType('application/json'); $this->response->setStatusCode(500); return json_encode(['error' => 'Invalid prunner configuration (could not read JWT secret)']); @@ -87,4 +92,26 @@ public function indexAction(string $path, string $subpath) return $response->getBody(); } + + /** + * @return string + */ + private function loadJwtSecretFromConfigFile(): string + { + if ($this->configFile && file_exists($this->configFile)) { + $path = $this->configFile; + } elseif ($this->directory && file_exists($this->directory . '/.prunner.yml')) { + $path = $this->directory . '/.prunner.yml'; + } else { + throw new \RuntimeException("Failed to locate prunner config file at " . $this->configFile . " or " . $this->directory . '/.prunner.yml'); + } + try { + // Try to parse prunner config to get JWT secret + $config = Yaml::parseFile($path); + $jwtSecret = $config['jwt_secret']; + } catch (ParseException $e) { + throw new \RuntimeException('Invalid prunner configuration (could not read JWT secret)'); + } + return $jwtSecret; + } } diff --git a/Classes/PrunnerApiService.php b/Classes/PrunnerApiService.php index 26cc522..5aecf19 100644 --- a/Classes/PrunnerApiService.php +++ b/Classes/PrunnerApiService.php @@ -32,6 +32,12 @@ class PrunnerApiService */ protected $directory; + /** + * @Flow\InjectConfiguration(path="configFile") + * @var string + */ + protected $configFile; + /** * @Flow\InjectConfiguration(path="jwtSecret") * @var string @@ -103,13 +109,7 @@ public function apiCall(string $method, string $subpath, ?string $body): Respons if (!empty($this->jwtSecret)) { $jwtSecret = $this->jwtSecret; } else { - try { - // Try to parse prunner config to get JWT secret - $config = Yaml::parseFile($this->directory . '/.prunner.yml'); - $jwtSecret = $config['jwt_secret']; - } catch (ParseException $e) { - throw new \RuntimeException('Invalid prunner configuration (could not read JWT secret)'); - } + $jwtSecret = $this->loadJwtSecretFromConfigFile(); } // There are usecases where we want to call prunner from the CLI. We don't have an initialized user there, thus we @@ -122,4 +122,25 @@ public function apiCall(string $method, string $subpath, ?string $body): Respons return $client->request($method, $url, ['headers' => ['Authorization' => 'Bearer ' . $authToken], 'body' => $body, 'http_errors' => false]); } + /** + * @return string + */ + private function loadJwtSecretFromConfigFile(): string + { + if ($this->configFile && file_exists($this->configFile)) { + $path = $this->configFile; + } elseif ($this->directory && file_exists($this->directory . '/.prunner.yml')) { + $path = $this->directory . '/.prunner.yml'; + } else { + throw new \RuntimeException("Failed to locate prunner config file at " . $this->configFile . " or " . $this->directory . '/.prunner.yml'); + } + try { + // Try to parse prunner config to get JWT secret + $config = Yaml::parseFile($path); + $jwtSecret = $config['jwt_secret']; + } catch (ParseException $e) { + throw new \RuntimeException('Invalid prunner configuration (could not read JWT secret)'); + } + return $jwtSecret; + } } diff --git a/Configuration/Settings.yaml b/Configuration/Settings.yaml index a60cf89..0306b3d 100644 --- a/Configuration/Settings.yaml +++ b/Configuration/Settings.yaml @@ -3,7 +3,10 @@ Flowpack: # Base URL to prunner API apiBaseUrl: 'http://localhost:9009/' # Working directory of prunner for loading config + # DEPRECATED: use configFile directory: '%FLOW_PATH_ROOT%' + # Path to the prunner config file to load the JWT secret for authentication + configFile: '%FLOW_PATH_ROOT%/.prunner.yml' # Explicitly set JWT secret if prunner config is not accessible jwtSecret: ~ From 9f8dbb7d65d68a83f670b2319f00e8c606f77c6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20D=C3=A4hne?= Date: Sun, 12 Nov 2023 15:16:52 +0100 Subject: [PATCH 3/4] FEATURE: convenience getter functions --- Classes/Dto/Jobs.php | 82 +++++++++++++++++++++++++++++++++---- Classes/Dto/Pipelines.php | 9 +++- Classes/Dto/TaskResults.php | 9 +++- 3 files changed, 91 insertions(+), 9 deletions(-) diff --git a/Classes/Dto/Jobs.php b/Classes/Dto/Jobs.php index e3ef2aa..0ddfe7f 100644 --- a/Classes/Dto/Jobs.php +++ b/Classes/Dto/Jobs.php @@ -10,6 +10,7 @@ */ class Jobs implements \IteratorAggregate { + /** * @var Job[] */ @@ -20,7 +21,6 @@ private function __construct(array $jobs) $this->jobs = $jobs; } - public static function fromJsonArray(array $in): self { $converted = []; @@ -42,6 +42,16 @@ public function forPipeline(PipelineName $pipeline): Jobs return new self($filteredJobs); } + /** + * @return Jobs all scheduled jobs not yet started + */ + public function waiting(): Jobs + { + return $this->filter(function (Job $job) { + return !$job->getStart(); + }); + } + /** * Filter running jobs * @@ -49,15 +59,73 @@ public function forPipeline(PipelineName $pipeline): Jobs */ public function running(): Jobs { - $filteredJobs = []; - foreach ($this->jobs as $job) { + return $this->filter(function (Job $job) { // running = started jobs which have not finished. - if ($job->getStart() !== null && !$job->getEnd()) { - $filteredJobs[] = $job; + return $job->getStart() !== null && !$job->getEnd(); + }); + } + + /** + * @return Jobs successful, canceled and failed jobs + */ + public function completed(): Jobs + { + return $this->filter(function (Job $job) { + return $job->isCompleted(); + }); + } + + /** + * @return Jobs successfully completed jobs + */ + public function successful(): Jobs + { + return $this->filter(function (Job $job) { + return $job->isCompleted() && !$job->isErrored() && !$job->isCanceled(); + }); + } + + /** + * @return Jobs canceled jobs + */ + public function canceled(): Jobs + { + return $this->filter(function (Job $job) { + return $job->isCanceled(); + }); + } + + /** + * @return Jobs failed jobs + */ + public function errored(): Jobs + { + return $this->filter(function (Job $job) { + return $job->isErrored(); + }); + } + + /** + * @param callable $predicate function(Job $job): bool + * @return Jobs all jobs where $predicate($job) + */ + public function filter(callable $predicate): Jobs + { + $result = []; + foreach ($this->jobs as $job) { + if ($predicate($job)) { + $result[] = $job; } } + return new self($result); + } - return new self($filteredJobs); + /** + * @return Job[] + */ + public function getArray(): array + { + return $this->jobs; } /** @@ -67,4 +135,4 @@ public function getIterator() { return new \ArrayIterator($this->jobs); } -} \ No newline at end of file +} diff --git a/Classes/Dto/Pipelines.php b/Classes/Dto/Pipelines.php index 9e22894..2ee09d1 100644 --- a/Classes/Dto/Pipelines.php +++ b/Classes/Dto/Pipelines.php @@ -24,6 +24,13 @@ public static function fromJsonArray(array $in): self return $pipelines; } + /** + * @return Pipeline[] + */ + public function getArray(): array + { + return $this->pipelines; + } /** * @return \Iterator @@ -32,4 +39,4 @@ public function getIterator() { return new \ArrayIterator($this->pipelines); } -} \ No newline at end of file +} diff --git a/Classes/Dto/TaskResults.php b/Classes/Dto/TaskResults.php index 1180697..2e168d5 100644 --- a/Classes/Dto/TaskResults.php +++ b/Classes/Dto/TaskResults.php @@ -138,6 +138,13 @@ public function get(string $taskName): ?TaskResult } + /** + * @return TaskResult[] + */ + public function getArray(): array { + return $this->taskResults; + } + /** * @return \Iterator */ @@ -156,4 +163,4 @@ public function allowsCallOfMethod($methodName) return true; } -} \ No newline at end of file +} From ff2d09fd2a4a90cef9fc095133d5a21fe847e626 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20D=C3=A4hne?= Date: Mon, 13 Nov 2023 11:48:58 +0100 Subject: [PATCH 4/4] TASK: fixed typing error in Jobs.php --- Classes/Dto/Job.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Classes/Dto/Job.php b/Classes/Dto/Job.php index 6c33b90..9a4dfbd 100644 --- a/Classes/Dto/Job.php +++ b/Classes/Dto/Job.php @@ -63,7 +63,7 @@ public static function fromJsonArray(array $in): self } /** - * @return string + * @return JobId */ public function getId(): JobId {