Compare commits
8 Commits
bb12655ca8
...
76baa2a1ae
Author | SHA1 | Date |
---|---|---|
Krzysztof Sikorski | 76baa2a1ae | |
Krzysztof Sikorski | 7800351f33 | |
Krzysztof Sikorski | f5033540b2 | |
Krzysztof Sikorski | b03fa78960 | |
Krzysztof Sikorski | f2a6935653 | |
Krzysztof Sikorski | ab7f5f0834 | |
Krzysztof Sikorski | 0089b21f66 | |
Krzysztof Sikorski | bb0d783357 |
|
@ -22,7 +22,8 @@
|
|||
},
|
||||
"require": {
|
||||
"php": "^8.3",
|
||||
"symfony/finder": "^7.0"
|
||||
"symfony/finder": "^7.0",
|
||||
"psr/cache": "^3.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Quintolin\Storage\Contract;
|
||||
|
||||
enum CacheKey: string
|
||||
{
|
||||
case RELEASE_NOTES = 'app.storage.release_notes';
|
||||
}
|
|
@ -4,47 +4,45 @@ declare(strict_types=1);
|
|||
|
||||
namespace Quintolin\Storage\Query;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use Quintolin\Storage\Contract\CacheKey;
|
||||
use Quintolin\Storage\QueryResult\ReleaseNote;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
use Symfony\Component\Finder\SplFileInfo;
|
||||
use Quintolin\Storage\Repository\ReleaseNoteRepository;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
abstract readonly class AbstractReleaseNotesQuery
|
||||
{
|
||||
public function __construct(
|
||||
private string $projectDir,
|
||||
protected ReleaseNoteRepository $repository,
|
||||
protected CacheItemPoolInterface $cacheItemPool,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @return ReleaseNote[]
|
||||
*/
|
||||
protected function fetch(null | int $maxCount): array
|
||||
protected function get(null | int $maxCount): array
|
||||
{
|
||||
$finder = new Finder();
|
||||
$finder->in(dirs: [$this->projectDir]);
|
||||
$finder->files();
|
||||
$finder->name(patterns: ['*.html']);
|
||||
$finder->sortByName();
|
||||
$finder->reverseSorting();
|
||||
$cacheKey = $this->getCacheKey(maxCount: $maxCount);
|
||||
$cacheItem = $this->cacheItemPool->getItem(key: $cacheKey);
|
||||
|
||||
$notes = [];
|
||||
$total = 0;
|
||||
/** @var SplFileInfo $file */
|
||||
foreach ($finder as $file) {
|
||||
$dateStr = $file->getBasename(suffix: '.html');
|
||||
$createdAt = DateTimeImmutable::createFromFormat('!Y-m-d', $dateStr);
|
||||
if ($createdAt instanceof DateTimeImmutable) {
|
||||
$id = sprintf('%d000', $createdAt->getTimestamp());
|
||||
$notes[] = new ReleaseNote(id: $id, createdAt: $createdAt, htmlContent: $file->getContents());
|
||||
++$total;
|
||||
}
|
||||
if ($maxCount > 0 && $total >= $maxCount) {
|
||||
break;
|
||||
}
|
||||
if ($cacheItem->isHit()) {
|
||||
return $cacheItem->get();
|
||||
}
|
||||
|
||||
$notes = $this->repository->fetch(maxCount: $maxCount);
|
||||
$cacheItem->set(value: $notes);
|
||||
$this->cacheItemPool->save($cacheItem);
|
||||
|
||||
return $notes;
|
||||
}
|
||||
|
||||
private function getCacheKey(null | int $maxCount): string
|
||||
{
|
||||
return sprintf(
|
||||
'%s_%s',
|
||||
CacheKey::RELEASE_NOTES->value,
|
||||
null !== $maxCount ? $maxCount : 'all',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ final readonly class LatestReleaseNoteQuery extends AbstractReleaseNotesQuery
|
|||
{
|
||||
public function __invoke(): null | ReleaseNote
|
||||
{
|
||||
$notes = $this->fetch(maxCount: 1);
|
||||
$notes = $this->get(maxCount: 1);
|
||||
return $notes[0] ?? null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,6 @@ final readonly class ReleaseNotesQuery extends AbstractReleaseNotesQuery
|
|||
*/
|
||||
public function __invoke(): array
|
||||
{
|
||||
return $this->fetch(maxCount: null);
|
||||
return $this->get(maxCount: null);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Quintolin\Storage\Repository;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use Quintolin\Storage\QueryResult\ReleaseNote;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
use Symfony\Component\Finder\SplFileInfo;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
final readonly class ReleaseNoteRepository
|
||||
{
|
||||
public function __construct(
|
||||
private string $projectDir,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @return ReleaseNote[]
|
||||
*/
|
||||
public function fetch(null | int $maxCount): array
|
||||
{
|
||||
$finder = new Finder();
|
||||
$finder->in(dirs: [$this->projectDir]);
|
||||
$finder->files();
|
||||
$finder->name(patterns: ['*.html']);
|
||||
$finder->sortByName();
|
||||
$finder->reverseSorting();
|
||||
|
||||
$notes = [];
|
||||
$total = 0;
|
||||
/** @var SplFileInfo $file */
|
||||
foreach ($finder as $file) {
|
||||
$dateStr = $file->getBasename(suffix: '.html');
|
||||
$createdAt = DateTimeImmutable::createFromFormat('!Y-m-d', $dateStr);
|
||||
if ($createdAt instanceof DateTimeImmutable) {
|
||||
$id = sprintf('%d000', $createdAt->getTimestamp());
|
||||
$notes[] = new ReleaseNote(id: $id, createdAt: $createdAt, htmlContent: $file->getContents());
|
||||
++$total;
|
||||
}
|
||||
if ($maxCount > 0 && $total >= $maxCount) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $notes;
|
||||
}
|
||||
}
|
|
@ -32,9 +32,9 @@
|
|||
"symfony/clock": "7.0.*",
|
||||
"symfony/console": "7.0.*",
|
||||
"symfony/dotenv": "7.0.*",
|
||||
"symfony/finder": "7.0.*",
|
||||
"symfony/flex": "^2",
|
||||
"symfony/framework-bundle": "7.0.*",
|
||||
"symfony/process": "7.0.*",
|
||||
"symfony/runtime": "7.0.*",
|
||||
"symfony/twig-bundle": "7.0.*",
|
||||
"symfony/yaml": "7.0.*",
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "eafe42c282ea9b4550d687735901053f",
|
||||
"content-hash": "01a78a0a3c014047539e06c2f37f5a9a",
|
||||
"packages": [
|
||||
{
|
||||
"name": "composer/semver",
|
||||
|
@ -383,10 +383,11 @@
|
|||
"dist": {
|
||||
"type": "path",
|
||||
"url": "../modules/storage",
|
||||
"reference": "40dd747426f7ffba283e67e03ffada0a9622ff11"
|
||||
"reference": "93e9e6864db9a5e765f69cee76194292fa809ee6"
|
||||
},
|
||||
"require": {
|
||||
"php": "^8.3",
|
||||
"psr/cache": "^3.0",
|
||||
"symfony/finder": "^7.0"
|
||||
},
|
||||
"type": "library",
|
||||
|
@ -2538,6 +2539,67 @@
|
|||
],
|
||||
"time": "2023-08-16T06:22:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/process",
|
||||
"version": "v7.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/process.git",
|
||||
"reference": "acd3eb5cb02382c1cb0287ba29b2908cc6ffa83a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/acd3eb5cb02382c1cb0287ba29b2908cc6ffa83a",
|
||||
"reference": "acd3eb5cb02382c1cb0287ba29b2908cc6ffa83a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.2"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\Process\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Executes commands in sub-processes",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/process/tree/v7.0.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-12-24T09:15:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/routing",
|
||||
"version": "v7.0.2",
|
||||
|
@ -3814,67 +3876,6 @@
|
|||
}
|
||||
],
|
||||
"time": "2023-10-31T18:23:49+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/process",
|
||||
"version": "v7.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/process.git",
|
||||
"reference": "acd3eb5cb02382c1cb0287ba29b2908cc6ffa83a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/acd3eb5cb02382c1cb0287ba29b2908cc6ffa83a",
|
||||
"reference": "acd3eb5cb02382c1cb0287ba29b2908cc6ffa83a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.2"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\Process\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Executes commands in sub-processes",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/process/tree/v7.0.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-12-24T09:15:37+00:00"
|
||||
}
|
||||
],
|
||||
"aliases": [],
|
||||
|
|
|
@ -3,16 +3,12 @@ services:
|
|||
autowire: true
|
||||
autoconfigure: true
|
||||
|
||||
Quintolin\Storage\Repository\:
|
||||
resource: '../../../modules/storage/src/Repository/'
|
||||
|
||||
Quintolin\Storage\Query\:
|
||||
resource: '../../../modules/storage/src/Query/'
|
||||
|
||||
Quintolin\Storage\Query\AbstractReleaseNotesQuery:
|
||||
abstract: true
|
||||
Quintolin\Storage\Repository\ReleaseNoteRepository:
|
||||
arguments:
|
||||
$projectDir: '%kernel.project_dir%/data/release-notes'
|
||||
|
||||
Quintolin\Storage\Query\LatestReleaseNoteQuery:
|
||||
parent: Quintolin\Storage\Query\AbstractReleaseNotesQuery
|
||||
|
||||
Quintolin\Storage\Query\ReleaseNotesQuery:
|
||||
parent: Quintolin\Storage\Query\AbstractReleaseNotesQuery
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Contract;
|
||||
|
||||
enum CacheKey: string
|
||||
{
|
||||
case APPLICATION_VERSION = 'app.version';
|
||||
}
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Service\VersionReader;
|
||||
use Quintolin\Core\HealthCheck as CoreHealthCheck;
|
||||
use Quintolin\Storage\HealthCheck as StorageHealthCheck;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
|
@ -16,12 +17,17 @@ use Symfony\Component\Routing\Attribute\Route;
|
|||
#[Route(path: '/health', name: 'app_health', methods: [Request::METHOD_GET])]
|
||||
final readonly class HealthController
|
||||
{
|
||||
public function __construct(
|
||||
private VersionReader $versionReader,
|
||||
) {}
|
||||
|
||||
public function __invoke(): Response
|
||||
{
|
||||
$coreChecker = new CoreHealthCheck();
|
||||
$storageChecker = new StorageHealthCheck();
|
||||
|
||||
return new JsonResponse(data: [
|
||||
'version' => ($this->versionReader)(),
|
||||
'core' => $coreChecker(),
|
||||
'storage' => $storageChecker(),
|
||||
]);
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use App\Contract\CacheKey;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
use function trim;
|
||||
|
||||
final class VersionReader
|
||||
{
|
||||
public function __construct(
|
||||
private CacheItemPoolInterface $cacheItemPool,
|
||||
) {}
|
||||
|
||||
public function __invoke(): string
|
||||
{
|
||||
$cacheKey = CacheKey::APPLICATION_VERSION->value;
|
||||
$cacheItem = $this->cacheItemPool->getItem(key: $cacheKey);
|
||||
|
||||
if ($cacheItem->isHit()) {
|
||||
return $cacheItem->get();
|
||||
}
|
||||
|
||||
$version = $this->fetch();
|
||||
|
||||
$cacheItem->set(value: $version);
|
||||
$this->cacheItemPool->save($cacheItem);
|
||||
|
||||
return $version;
|
||||
}
|
||||
|
||||
private function fetch(): string
|
||||
{
|
||||
$process = new Process(command: ['git', 'describe', '--always']);
|
||||
$process->run();
|
||||
return trim(string: $process->getOutput());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Twig;
|
||||
|
||||
use App\Service\VersionReader;
|
||||
use Override;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
use Twig\TwigFunction;
|
||||
|
||||
final class AppVersionExtension extends AbstractExtension
|
||||
{
|
||||
public function __construct(
|
||||
private VersionReader $versionReader,
|
||||
) {}
|
||||
|
||||
#[Override]
|
||||
public function getFunctions(): array
|
||||
{
|
||||
return [
|
||||
new TwigFunction('app_version', [$this, 'getVersion']),
|
||||
];
|
||||
}
|
||||
|
||||
public function getVersion(): string
|
||||
{
|
||||
return ($this->versionReader)();
|
||||
}
|
||||
}
|
|
@ -26,8 +26,10 @@
|
|||
{%- set current_datetime_human = current_datetime|format_datetime(pattern='yyyy-MM-dd, HH:mm:ss z') -%}
|
||||
<footer>
|
||||
<p>
|
||||
<b>Server time:</b>
|
||||
<b>Server Time:</b>
|
||||
<time datetime="{{ current_datetime_machine }}">{{ current_datetime_human }}</time>
|
||||
<b>Game Engine version:</b>
|
||||
<code>{{ app_version() }}</code>
|
||||
</p>
|
||||
</footer>
|
||||
</body>
|
||||
|
|
Reference in New Issue