From 1eedf11324fdb502af8f99aa65aed3217122667a Mon Sep 17 00:00:00 2001 From: "m::r" Date: Thu, 8 Jan 2026 00:02:14 +0000 Subject: [PATCH 1/3] feat(version): show version in health endpoint --- .docker/php-fpm/Dockerfile | 3 ++- .github/workflows/pipeline.yml | 1 + app/src/Flags/Controller/HealthController.php | 14 +++++++++++++- app/src/Flags/Security/HqAuthAuthenticator.php | 17 ++--------------- 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/.docker/php-fpm/Dockerfile b/.docker/php-fpm/Dockerfile index 42bd189..4eaecc6 100644 --- a/.docker/php-fpm/Dockerfile +++ b/.docker/php-fpm/Dockerfile @@ -141,7 +141,8 @@ RUN { \ COPY --from=builder --chown=www-data:www-data /var/www/html ./ ARG SENTRY_RELEASE="" -ENV APP_ENV=prod APP_DEBUG=0 SENTRY_RELEASE=${SENTRY_RELEASE} +ARG APP_VERSION="" +ENV APP_ENV=prod APP_DEBUG=0 SENTRY_RELEASE=${SENTRY_RELEASE} APP_VERSION=${APP_VERSION} # Create minimal .env for Symfony bootstrap, placeholder for public key, remove dev files RUN printf "APP_ENV=prod\nSENTRY_DSN=\nJWKS_URI=https://auth.izeebot.top/.well-known/jwks.json\n" > .env \ diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml index 5afcad3..0a965ab 100644 --- a/.github/workflows/pipeline.yml +++ b/.github/workflows/pipeline.yml @@ -210,6 +210,7 @@ jobs: labels: ${{ steps.meta-php.outputs.labels }} build-args: | SENTRY_RELEASE=${{ vars.SENTRY_PROJECT }}@${{ needs.version.outputs.new_version }} + APP_VERSION=v${{ needs.version.outputs.new_version }} no-cache: true - name: Build and push Caddy diff --git a/app/src/Flags/Controller/HealthController.php b/app/src/Flags/Controller/HealthController.php index a7d8415..b06417b 100644 --- a/app/src/Flags/Controller/HealthController.php +++ b/app/src/Flags/Controller/HealthController.php @@ -15,7 +15,10 @@ final class HealthController extends AbstractController #[Route('/health', name: 'health_check', methods: ['GET'])] public function health(): JsonResponse { - return new JsonResponse(['status' => 'ok'], Response::HTTP_OK); + return new JsonResponse([ + 'status' => 'ok', + 'version' => $this->getVersion(), + ], Response::HTTP_OK); } #[Route('/health/ready', name: 'health_ready', methods: ['GET'])] @@ -32,12 +35,21 @@ public function ready(Connection $connection): JsonResponse return new JsonResponse([ 'status' => 'ok' === $dbStatus ? 'ok' : 'degraded', + 'version' => $this->getVersion(), 'checks' => [ 'database' => $dbStatus, ], ], $status); } + private function getVersion(): array + { + return [ + 'version' => $_ENV['APP_VERSION'] ?? null, + 'environment' => $this->getParameter('kernel.environment'), + ]; + } + #[Route('/robots.txt', name: 'robots_txt', methods: ['GET'])] public function robots(): Response { diff --git a/app/src/Flags/Security/HqAuthAuthenticator.php b/app/src/Flags/Security/HqAuthAuthenticator.php index 26565f3..4e2c197 100644 --- a/app/src/Flags/Security/HqAuthAuthenticator.php +++ b/app/src/Flags/Security/HqAuthAuthenticator.php @@ -71,7 +71,6 @@ public function authenticate(Request $request): Passport #[\Override] public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response { - // dd($request->toArray()); // Get the JWT access token $accessToken = $request->attributes->get('oauth_access_token'); $refreshToken = $request->attributes->get('oauth_refresh_token'); @@ -81,23 +80,11 @@ public function onAuthenticationSuccess(Request $request, TokenInterface $token, window.opener.postMessage({ type: 'oauth_success', access_token: '$accessToken', - refresh_token: '$refreshToken' + refresh_token: '$refreshToken', + expires_in: '$expiresIn', }, '*'); window.close(); "); - - // Return JSON with the tokens for the frontend - // return new JsonResponse([ - // 'success' => true, - // 'access_token' => $accessToken, - // 'refresh_token' => $refreshToken, - // 'expires_in' => $expiresIn, - // 'token_type' => 'Bearer', - // 'user' => [ - // // 'email' => $token->getUser()->getEmail(), - // 'roles' => $token->getUser()->getRoles(), - // ] - // ]); } #[\Override] From 8dbed56182bf2a5742266d8455a922e26f3e881d Mon Sep 17 00:00:00 2001 From: "m::r" Date: Thu, 8 Jan 2026 00:05:24 +0000 Subject: [PATCH 2/3] fix: cs --- app/psalm-baseline.xml | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/psalm-baseline.xml b/app/psalm-baseline.xml index 0f300d4..992fe6a 100644 --- a/app/psalm-baseline.xml +++ b/app/psalm-baseline.xml @@ -227,9 +227,6 @@ - - - From dd77e524185fcc6416b8a6581ed679be86915be0 Mon Sep 17 00:00:00 2001 From: "m::r" Date: Thu, 8 Jan 2026 00:23:49 +0000 Subject: [PATCH 3/3] fix: test --- app/Makefile | 5 ++- app/config/services.yaml | 1 + app/src/Flags/Controller/HealthController.php | 2 +- .../Unit/Controller/HealthControllerTest.php | 34 +++++++++++++++---- 4 files changed, 34 insertions(+), 8 deletions(-) diff --git a/app/Makefile b/app/Makefile index 6423518..6acad77 100644 --- a/app/Makefile +++ b/app/Makefile @@ -41,7 +41,7 @@ composer: ## Install PHP dependencies cache: ## Clear Symfony cache @docker compose exec php bin/console c:c -test-build: +test-build: ## Build test images @$(TEST_COMPOSE) build test: ## Run PHPUnit tests (isolated test containers) @echo "Starting test containers..." @@ -167,5 +167,8 @@ network: ## Create Docker network t: ## Quick test filter (uses test containers): make t -- CorrectFlagEndpointTest @$(TEST_COMPOSE) exec php vendor/bin/phpunit --filter "$(filter-out $@,$(MAKECMDGOALS) $(MAKEFLAGS))" +cc: ## Delete all cache folders + rm -rf var/cache + %: @ \ No newline at end of file diff --git a/app/config/services.yaml b/app/config/services.yaml index 6cc13ce..11d9b67 100644 --- a/app/config/services.yaml +++ b/app/config/services.yaml @@ -5,6 +5,7 @@ # https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration parameters: jwks_uri: '%env(JWKS_URI)%' + app.version: '%env(default::APP_VERSION)%' services: # default configuration for services in *this* file diff --git a/app/src/Flags/Controller/HealthController.php b/app/src/Flags/Controller/HealthController.php index b06417b..8220778 100644 --- a/app/src/Flags/Controller/HealthController.php +++ b/app/src/Flags/Controller/HealthController.php @@ -45,7 +45,7 @@ public function ready(Connection $connection): JsonResponse private function getVersion(): array { return [ - 'version' => $_ENV['APP_VERSION'] ?? null, + 'version' => $this->getParameter('app.version'), 'environment' => $this->getParameter('kernel.environment'), ]; } diff --git a/app/tests/Unit/Controller/HealthControllerTest.php b/app/tests/Unit/Controller/HealthControllerTest.php index 4a34d09..b9579e8 100644 --- a/app/tests/Unit/Controller/HealthControllerTest.php +++ b/app/tests/Unit/Controller/HealthControllerTest.php @@ -6,19 +6,35 @@ use App\Flags\Controller\HealthController; use Doctrine\DBAL\Connection; -use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; +use Symfony\Component\DependencyInjection\Container; use Symfony\Component\HttpFoundation\Response; -class HealthControllerTest extends TestCase +class HealthControllerTest extends KernelTestCase { + private Container $container; + + protected function setUp(): void + { + self::bootKernel(); + $this->container = static::getContainer(); + } + public function testHealthReturnsOk(): void { - $controller = new HealthController(); + /** @var HealthController $controller */ + $controller = $this->container->get(HealthController::class); + $controller->setContainer($this->container); $response = $controller->health(); $this->assertEquals(Response::HTTP_OK, $response->getStatusCode()); - $this->assertEquals('{"status":"ok"}', $response->getContent()); + + $content = json_decode($response->getContent(), true); + $this->assertEquals('ok', $content['status']); + $this->assertArrayHasKey('version', $content); + $this->assertIsArray($content['version']); + $this->assertArrayHasKey('environment', $content['version']); } public function testReadyReturnsOkWhenDatabaseIsConnected(): void @@ -26,7 +42,9 @@ public function testReadyReturnsOkWhenDatabaseIsConnected(): void $connection = $this->createMock(Connection::class); $connection->method('executeQuery')->willReturn($this->createMock(\Doctrine\DBAL\Result::class)); - $controller = new HealthController(); + /** @var HealthController $controller */ + $controller = $this->container->get(HealthController::class); + $controller->setContainer($this->container); $response = $controller->ready($connection); @@ -34,6 +52,7 @@ public function testReadyReturnsOkWhenDatabaseIsConnected(): void $content = json_decode($response->getContent(), true); $this->assertEquals('ok', $content['status']); + $this->assertArrayHasKey('version', $content); $this->assertEquals('ok', $content['checks']['database']); } @@ -42,7 +61,9 @@ public function testReadyReturnsServiceUnavailableWhenDatabaseFails(): void $connection = $this->createMock(Connection::class); $connection->method('executeQuery')->willThrowException(new \Exception('Connection failed')); - $controller = new HealthController(); + /** @var HealthController $controller */ + $controller = $this->container->get(HealthController::class); + $controller->setContainer($this->container); $response = $controller->ready($connection); @@ -50,6 +71,7 @@ public function testReadyReturnsServiceUnavailableWhenDatabaseFails(): void $content = json_decode($response->getContent(), true); $this->assertEquals('degraded', $content['status']); + $this->assertArrayHasKey('version', $content); $this->assertEquals('error', $content['checks']['database']); } }