mirror of
https://github.com/MarijnDoeve/TijdVoorDeTest.git
synced 2026-07-05 15:10:16 +02:00
Compare commits
2 Commits
0.1.0
...
815e7b17be
| Author | SHA1 | Date | |
|---|---|---|---|
| 815e7b17be | |||
| 764f59e6a7 |
+60
-15
@@ -17,12 +17,11 @@ permissions:
|
|||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
tests:
|
quality:
|
||||||
name: Tests
|
name: Code Quality
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 20
|
||||||
permissions:
|
permissions:
|
||||||
checks: write
|
|
||||||
pull-requests: write
|
|
||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@@ -40,26 +39,68 @@ jobs:
|
|||||||
compose.yaml
|
compose.yaml
|
||||||
compose.override.yaml
|
compose.override.yaml
|
||||||
set: |
|
set: |
|
||||||
*.cache-from=type=gha,scope=${{github.ref}}
|
*.cache-from=type=gha,scope=${{github.ref}}-quality
|
||||||
*.cache-from=type=gha,scope=refs/heads/main
|
*.cache-from=type=gha,scope=refs/heads/main
|
||||||
*.cache-to=type=gha,scope=${{github.ref}},mode=max
|
*.cache-to=type=gha,scope=${{github.ref}}-quality,mode=${{ github.event_name == 'pull_request' && 'min' || 'max' }}
|
||||||
- name: Start services
|
- name: Start services
|
||||||
run: docker compose up php database --wait --no-build
|
run: docker compose up php database --wait --no-build
|
||||||
|
- name: Warm up dev cache
|
||||||
|
run: docker compose exec -T php bin/console cache:warmup --env=dev
|
||||||
- name: Lint Twig templates
|
- name: Lint Twig templates
|
||||||
|
id: twig_lint
|
||||||
|
continue-on-error: true
|
||||||
run: docker compose exec -T php bin/console lint:twig --format=github templates
|
run: docker compose exec -T php bin/console lint:twig --format=github templates
|
||||||
- name: Coding Style
|
- name: Coding Style
|
||||||
|
id: cs
|
||||||
|
continue-on-error: true
|
||||||
run: docker compose exec -T php vendor/bin/php-cs-fixer check --diff --show-progress=none
|
run: docker compose exec -T php vendor/bin/php-cs-fixer check --diff --show-progress=none
|
||||||
- name: Twig Coding Style
|
- name: Twig Coding Style
|
||||||
|
id: twig_cs
|
||||||
|
continue-on-error: true
|
||||||
run: docker compose exec -T php vendor/bin/twig-cs-fixer check
|
run: docker compose exec -T php vendor/bin/twig-cs-fixer check
|
||||||
- name: Static Analysis (PHPStan)
|
- name: Static Analysis (PHPStan)
|
||||||
|
id: phpstan
|
||||||
|
continue-on-error: true
|
||||||
run: docker compose exec -T php vendor/bin/phpstan analyse --no-progress --no-ansi --error-format=github
|
run: docker compose exec -T php vendor/bin/phpstan analyse --no-progress --no-ansi --error-format=github
|
||||||
- name: Rector
|
- name: Rector
|
||||||
|
id: rector
|
||||||
|
continue-on-error: true
|
||||||
run: docker compose exec -T php vendor/bin/rector process --dry-run --no-progress-bar --output-format=github
|
run: docker compose exec -T php vendor/bin/rector process --dry-run --no-progress-bar --output-format=github
|
||||||
- name: Check HTTP reachability
|
- name: Check HTTP reachability
|
||||||
run: curl -v --fail-with-body http://localhost
|
run: curl -v --fail-with-body http://localhost
|
||||||
- name: Check Mercure reachability
|
- name: Assert all checks passed
|
||||||
if: false
|
if: always()
|
||||||
run: curl -vkI --fail-with-body https://localhost/.well-known/mercure?topic=test
|
run: |
|
||||||
|
outcomes="${{ steps.twig_lint.outcome }} ${{ steps.cs.outcome }} ${{ steps.twig_cs.outcome }} ${{ steps.phpstan.outcome }} ${{ steps.rector.outcome }}"
|
||||||
|
if echo "$outcomes" | grep -q "failure"; then exit 1; fi
|
||||||
|
|
||||||
|
tests:
|
||||||
|
name: Tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 20
|
||||||
|
permissions:
|
||||||
|
checks: write
|
||||||
|
pull-requests: write
|
||||||
|
contents: read
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
- name: Build Docker images
|
||||||
|
uses: docker/bake-action@v5
|
||||||
|
with:
|
||||||
|
pull: true
|
||||||
|
load: true
|
||||||
|
files: |
|
||||||
|
compose.yaml
|
||||||
|
compose.override.yaml
|
||||||
|
set: |
|
||||||
|
*.cache-from=type=gha,scope=${{github.ref}}-tests
|
||||||
|
*.cache-from=type=gha,scope=refs/heads/main
|
||||||
|
*.cache-to=type=gha,scope=${{github.ref}}-tests,mode=${{ github.event_name == 'pull_request' && 'min' || 'max' }}
|
||||||
|
- name: Start services
|
||||||
|
run: docker compose up php database --wait --no-build
|
||||||
- name: Create test database
|
- name: Create test database
|
||||||
run: docker compose exec -T php bin/console -e test doctrine:database:create
|
run: docker compose exec -T php bin/console -e test doctrine:database:create
|
||||||
- name: Run migrations
|
- name: Run migrations
|
||||||
@@ -78,15 +119,16 @@ jobs:
|
|||||||
run: docker compose exec -T php bin/console -e test doctrine:schema:validate
|
run: docker compose exec -T php bin/console -e test doctrine:schema:validate
|
||||||
|
|
||||||
build-deploy:
|
build-deploy:
|
||||||
name: Build and deploy to ${{ startsWith(github.ref, 'refs/tags/') && 'production' || (github.ref == 'refs/heads/main' && 'acceptance' || '') }}
|
name: Build and Deploy
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
environment:
|
environment:
|
||||||
name: ${{ startsWith(github.ref, 'refs/tags/') && 'production' || (github.ref == 'refs/heads/main' && 'acceptance' || '') }}
|
name: ${{ startsWith(github.ref, 'refs/tags/') && 'production' || 'acceptance' }}
|
||||||
url: ${{ vars.URL }}
|
url: ${{ vars.URL }}
|
||||||
needs: tests
|
needs: [quality, tests]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 15
|
||||||
if: (github.ref == 'refs/heads/main' && false) || startsWith(github.ref, 'refs/tags/')
|
if: (github.ref == 'refs/heads/main' && false) || startsWith(github.ref, 'refs/tags/')
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@@ -106,14 +148,17 @@ jobs:
|
|||||||
REPO_LOWER=$(echo "${{ github.repository }}" | tr '[:upper:]' '[:lower:]')
|
REPO_LOWER=$(echo "${{ github.repository }}" | tr '[:upper:]' '[:lower:]')
|
||||||
if [[ "${{ github.ref }}" == refs/tags/* ]]; then
|
if [[ "${{ github.ref }}" == refs/tags/* ]]; then
|
||||||
TAG="${GITHUB_REF#refs/tags/}"
|
TAG="${GITHUB_REF#refs/tags/}"
|
||||||
|
SENTRY_VERSION="${TAG#v}"
|
||||||
{
|
{
|
||||||
echo "tag=$TAG"
|
echo "tag=$TAG"
|
||||||
|
echo "sentry_version=$SENTRY_VERSION"
|
||||||
echo "full_name=ghcr.io/${REPO_LOWER}:$TAG"
|
echo "full_name=ghcr.io/${REPO_LOWER}:$TAG"
|
||||||
} >> "$GITHUB_OUTPUT"
|
} >> "$GITHUB_OUTPUT"
|
||||||
else
|
else
|
||||||
SHORT_SHA=$(git rev-parse --short HEAD)
|
SHORT_SHA=$(git rev-parse --short HEAD)
|
||||||
{
|
{
|
||||||
echo "tag=$SHORT_SHA"
|
echo "tag=$SHORT_SHA"
|
||||||
|
echo "sentry_version=$SHORT_SHA"
|
||||||
echo "full_name=ghcr.io/${REPO_LOWER}:$SHORT_SHA"
|
echo "full_name=ghcr.io/${REPO_LOWER}:$SHORT_SHA"
|
||||||
} >> "$GITHUB_OUTPUT"
|
} >> "$GITHUB_OUTPUT"
|
||||||
fi
|
fi
|
||||||
@@ -139,12 +184,12 @@ jobs:
|
|||||||
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
|
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
|
||||||
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
|
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
|
||||||
with:
|
with:
|
||||||
release: ${{steps.meta.outputs.tag}}
|
release: ${{steps.meta.outputs.sentry_version}}
|
||||||
environment: ${{ startsWith(github.ref, 'refs/tags/') && 'production' || (github.ref == 'refs/heads/main' && 'acceptance' || '') }}
|
environment: ${{ startsWith(github.ref, 'refs/tags/') && 'production' || 'acceptance' }}
|
||||||
|
|
||||||
- name: Trigger Portainer Deployment
|
- name: Trigger Portainer Deployment
|
||||||
shell: bash
|
shell: bash
|
||||||
env:
|
env:
|
||||||
PORTAINER_WEBHOOK: ${{secrets.PORTAINER_WEBHOOK}}
|
PORTAINER_WEBHOOK: ${{secrets.PORTAINER_WEBHOOK}}
|
||||||
run: |
|
run: |
|
||||||
curl -v -X POST "$PORTAINER_WEBHOOK"?IMAGE_TAG=${{steps.meta.outputs.tag}} --fail-with-body
|
curl -v -X POST "${PORTAINER_WEBHOOK}?IMAGE_TAG=${{steps.meta.outputs.tag}}" --fail-with-body
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{% if is_granted('IS_AUTHENTICATED') or app.current_route() == 'tvdt_quiz_select_season' %}
|
||||||
|
<div class="quiz-topbar">
|
||||||
|
{% if is_granted('IS_AUTHENTICATED') %}
|
||||||
|
<a href="{{ path('tvdt_backoffice_index') }}" class="btn btn-outline-secondary btn-sm">
|
||||||
|
{{ 'Backoffice'|trans }}
|
||||||
|
</a>
|
||||||
|
<a href="{{ path('tvdt_login_logout') }}" class="btn btn-outline-secondary btn-sm">
|
||||||
|
{{ 'Logout'|trans }}
|
||||||
|
</a>
|
||||||
|
{% else %}
|
||||||
|
<a href="{{ path('tvdt_backoffice_index') }}" class="btn btn-outline-secondary btn-sm">
|
||||||
|
{{ 'Manage Quiz'|trans }}
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Tvdt\Tests\Twig;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\Assert;
|
||||||
|
use PHPUnit\Framework\Attributes\DataProvider;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
use function Safe\file_get_contents;
|
||||||
|
use function Safe\preg_match_all;
|
||||||
|
|
||||||
|
final class TemplateReferencesTest extends TestCase
|
||||||
|
{
|
||||||
|
private static string $templatesDir;
|
||||||
|
|
||||||
|
public static function setUpBeforeClass(): void
|
||||||
|
{
|
||||||
|
self::$templatesDir = \dirname(__DIR__, 2).'/templates';
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return iterable<string, array{string, string}> */
|
||||||
|
public static function templateReferenceProvider(): iterable
|
||||||
|
{
|
||||||
|
$templatesDir = \dirname(__DIR__, 2).'/templates';
|
||||||
|
$iterator = new \RecursiveIteratorIterator(
|
||||||
|
new \RecursiveDirectoryIterator($templatesDir, \RecursiveDirectoryIterator::SKIP_DOTS),
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($iterator as $file) {
|
||||||
|
Assert::assertInstanceOf(\SplFileInfo::class, $file);
|
||||||
|
if ('twig' !== $file->getExtension()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$content = file_get_contents($file->getPathname());
|
||||||
|
$sourceFile = str_replace($templatesDir.'/', '', $file->getPathname());
|
||||||
|
|
||||||
|
// Match extends, include(), and embed tags — capture the quoted template name
|
||||||
|
preg_match_all(
|
||||||
|
'/(?:extends|include|embed)\s*\(?[\'"]([^\'"]+)[\'"]\)?/',
|
||||||
|
$content,
|
||||||
|
$matches,
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($matches[1] as $referencedTemplate) {
|
||||||
|
yield \sprintf('%s → %s', $sourceFile, $referencedTemplate) => [$sourceFile, $referencedTemplate];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[DataProvider('templateReferenceProvider')]
|
||||||
|
public function testReferencedTemplateExists(string $sourceFile, string $referencedTemplate): void
|
||||||
|
{
|
||||||
|
$absolutePath = self::$templatesDir.'/'.$referencedTemplate;
|
||||||
|
|
||||||
|
$this->assertFileExists(
|
||||||
|
$absolutePath,
|
||||||
|
\sprintf("Template '%s' references '%s' which does not exist at '%s'.", $sourceFile, $referencedTemplate, $absolutePath),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user