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/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/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 @@
-
-
-
diff --git a/app/src/Flags/Controller/HealthController.php b/app/src/Flags/Controller/HealthController.php
index a7d8415..8220778 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' => $this->getParameter('app.version'),
+ '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]
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']);
}
}