Compare commits

...

3 Commits

Author SHA1 Message Date
dependabot[bot] 2f67a6b852 Bump the dev-dependencies group across 1 directory with 10 updates
Bumps the dev-dependencies group with 10 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [friendsofphp/php-cs-fixer](https://github.com/PHP-CS-Fixer/PHP-CS-Fixer) | `3.95.8` | `3.95.11` |
| [phpstan/phpstan](https://github.com/phpstan/phpstan-phar-composer-source) | `2.2.2` | `2.2.3` |
| [phpstan/phpstan-phpunit](https://github.com/phpstan/phpstan-phpunit) | `2.0.16` | `2.0.17` |
| [phpunit/phpunit](https://github.com/sebastianbergmann/phpunit) | `13.2.1` | `13.2.2` |
| [rector/rector](https://github.com/rectorphp/rector) | `2.4.6` | `2.5.2` |
| [symfony/browser-kit](https://github.com/symfony/browser-kit) | `8.1.0` | `8.1.1` |
| [symfony/phpunit-bridge](https://github.com/symfony/phpunit-bridge) | `8.1.0` | `8.1.1` |
| [symfony/web-profiler-bundle](https://github.com/symfony/web-profiler-bundle) | `8.1.0` | `8.1.1` |
| [thecodingmachine/phpstan-safe-rule](https://github.com/thecodingmachine/phpstan-safe-rule) | `1.4.3` | `1.4.7` |
| [vincentlanglet/twig-cs-fixer](https://github.com/VincentLanglet/Twig-CS-Fixer) | `4.0.1` | `4.0.2` |



Updates `friendsofphp/php-cs-fixer` from 3.95.8 to 3.95.11
- [Release notes](https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/releases)
- [Changelog](https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/compare/v3.95.8...v3.95.11)

Updates `phpstan/phpstan` from 2.2.2 to 2.2.3
- [Commits](https://github.com/phpstan/phpstan-phar-composer-source/commits)

Updates `phpstan/phpstan-phpunit` from 2.0.16 to 2.0.17
- [Release notes](https://github.com/phpstan/phpstan-phpunit/releases)
- [Commits](https://github.com/phpstan/phpstan-phpunit/compare/2.0.16...2.0.17)

Updates `phpunit/phpunit` from 13.2.1 to 13.2.2
- [Release notes](https://github.com/sebastianbergmann/phpunit/releases)
- [Changelog](https://github.com/sebastianbergmann/phpunit/blob/13.2.2/ChangeLog-13.2.md)
- [Commits](https://github.com/sebastianbergmann/phpunit/compare/13.2.1...13.2.2)

Updates `rector/rector` from 2.4.6 to 2.5.2
- [Release notes](https://github.com/rectorphp/rector/releases)
- [Commits](https://github.com/rectorphp/rector/compare/2.4.6...2.5.2)

Updates `symfony/browser-kit` from 8.1.0 to 8.1.1
- [Release notes](https://github.com/symfony/browser-kit/releases)
- [Changelog](https://github.com/symfony/browser-kit/blob/8.2/CHANGELOG.md)
- [Commits](https://github.com/symfony/browser-kit/compare/v8.1.0...v8.1.1)

Updates `symfony/phpunit-bridge` from 8.1.0 to 8.1.1
- [Release notes](https://github.com/symfony/phpunit-bridge/releases)
- [Changelog](https://github.com/symfony/phpunit-bridge/blob/8.2/CHANGELOG.md)
- [Commits](https://github.com/symfony/phpunit-bridge/compare/v8.1.0...v8.1.1)

Updates `symfony/web-profiler-bundle` from 8.1.0 to 8.1.1
- [Release notes](https://github.com/symfony/web-profiler-bundle/releases)
- [Changelog](https://github.com/symfony/web-profiler-bundle/blob/8.2/CHANGELOG.md)
- [Commits](https://github.com/symfony/web-profiler-bundle/compare/v8.1.0...v8.1.1)

Updates `thecodingmachine/phpstan-safe-rule` from 1.4.3 to 1.4.7
- [Release notes](https://github.com/thecodingmachine/phpstan-safe-rule/releases)
- [Commits](https://github.com/thecodingmachine/phpstan-safe-rule/compare/v1.4.3...v1.4.7)

Updates `vincentlanglet/twig-cs-fixer` from 4.0.1 to 4.0.2
- [Release notes](https://github.com/VincentLanglet/Twig-CS-Fixer/releases)
- [Commits](https://github.com/VincentLanglet/Twig-CS-Fixer/compare/4.0.1...4.0.2)

---
updated-dependencies:
- dependency-name: friendsofphp/php-cs-fixer
  dependency-version: 3.95.11
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: phpstan/phpstan
  dependency-version: 2.2.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: phpstan/phpstan-phpunit
  dependency-version: 2.0.17
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: phpunit/phpunit
  dependency-version: 13.2.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: rector/rector
  dependency-version: 2.5.2
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dev-dependencies
- dependency-name: symfony/browser-kit
  dependency-version: 8.1.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: symfony/phpunit-bridge
  dependency-version: 8.1.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: symfony/web-profiler-bundle
  dependency-version: 8.1.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: thecodingmachine/phpstan-safe-rule
  dependency-version: 1.4.7
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: vincentlanglet/twig-cs-fixer
  dependency-version: 4.0.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-02 23:53:41 +00:00
Marijn 815e7b17be Add missing quiz/nav.html.twig and test for template reference integrity (#166)
Adds the previously uncommitted quiz nav partial that broke main, and
introduces TemplateReferencesTest which scans all Twig templates for
extends/include/embed references and asserts each target file exists —
preventing this class of missing-template mistake from reaching CI undetected.
2026-07-02 21:08:21 +00:00
Marijn 764f59e6a7 Improve GitHub Actions CI: parallelise jobs, continue-on-error, timeouts, cache optimisation (#165)
* Strip v-prefix from version tag before passing to Sentry

GitHub tags follow the v1.2.3 convention, but Sentry requires bare
semver (1.2.3) to recognise releases as valid semver. Extract a
sentry_version output in the meta step that strips the leading v.

* Parallelize CI: split quality and tests jobs, add continue-on-error

- Split the single tests job into parallel quality and tests jobs,
  saving ~4 min wall-clock time per run
- Quality checks (lint, CS, PHPStan, Rector) now all run with
  continue-on-error so every failure is visible in one pass; a
  final Assert step fails the job if any check failed
- Add cache:warmup before PHPStan so the Symfony dev container XML
  exists and the Symfony extension has full type information
- Use per-job GHA cache scopes to avoid parallel cache write races
- Use cache mode=min on PRs, mode=max on main/tags
- Add timeout-minutes (20/20/15) to all jobs
- Remove dead if:false Mercure reachability step
- Fix Portainer webhook URL quoting
- build-deploy now needs: [quality, tests]

* Simplify build-deploy job name and environment expressions

* Use static name for build-deploy job (expressions not evaluated when skipped)

* build-deploy only needs tests, not quality (quality is informational)

* Revert: build-deploy needs both quality and tests
2026-07-02 23:06:23 +02:00
4 changed files with 211 additions and 85 deletions
+60 -15
View File
@@ -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
Generated
+71 -69
View File
@@ -9287,16 +9287,16 @@
}, },
{ {
"name": "friendsofphp/php-cs-fixer", "name": "friendsofphp/php-cs-fixer",
"version": "v3.95.8", "version": "v3.95.11",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
"reference": "4140023f552ff02346df9b1329742532166f677f" "reference": "35f98e1293283397824d7f349ce5afb8747c3cd5"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/4140023f552ff02346df9b1329742532166f677f", "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/35f98e1293283397824d7f349ce5afb8747c3cd5",
"reference": "4140023f552ff02346df9b1329742532166f677f", "reference": "35f98e1293283397824d7f349ce5afb8747c3cd5",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -9330,7 +9330,7 @@
"require-dev": { "require-dev": {
"facile-it/paraunit": "^1.3.1 || ^2.11.0", "facile-it/paraunit": "^1.3.1 || ^2.11.0",
"infection/infection": "^0.32.7", "infection/infection": "^0.32.7",
"justinrainbow/json-schema": "^6.9.0", "justinrainbow/json-schema": "^6.10.0",
"keradus/cli-executor": "^2.3", "keradus/cli-executor": "^2.3",
"mikey179/vfsstream": "^1.6.12", "mikey179/vfsstream": "^1.6.12",
"php-coveralls/php-coveralls": "^2.9.1", "php-coveralls/php-coveralls": "^2.9.1",
@@ -9380,7 +9380,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.95.8" "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.95.11"
}, },
"funding": [ "funding": [
{ {
@@ -9388,7 +9388,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2026-06-16T09:52:26+00:00" "time": "2026-06-25T14:17:04+00:00"
}, },
{ {
"name": "myclabs/deep-copy", "name": "myclabs/deep-copy",
@@ -9676,11 +9676,11 @@
}, },
{ {
"name": "phpstan/phpstan", "name": "phpstan/phpstan",
"version": "2.2.2", "version": "2.2.3",
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/e5cc34d491a90e79c216d824f60fe21fd4d93bd6", "url": "https://api.github.com/repos/phpstan/phpstan/zipball/4048833dd47b377287818841877fb3087289509c",
"reference": "e5cc34d491a90e79c216d824f60fe21fd4d93bd6", "reference": "4048833dd47b377287818841877fb3087289509c",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -9736,7 +9736,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2026-06-05T09:00:01+00:00" "time": "2026-06-30T21:15:26+00:00"
}, },
{ {
"name": "phpstan/phpstan-doctrine", "name": "phpstan/phpstan-doctrine",
@@ -9817,21 +9817,22 @@
}, },
{ {
"name": "phpstan/phpstan-phpunit", "name": "phpstan/phpstan-phpunit",
"version": "2.0.16", "version": "2.0.17",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpstan/phpstan-phpunit.git", "url": "https://github.com/phpstan/phpstan-phpunit.git",
"reference": "6ab598e1bc106e6827fd346ae4a12b4a5d634c32" "reference": "c2f977551f0736d60467b3d754b2e0cf4e337b3f"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/6ab598e1bc106e6827fd346ae4a12b4a5d634c32", "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/c2f977551f0736d60467b3d754b2e0cf4e337b3f",
"reference": "6ab598e1bc106e6827fd346ae4a12b4a5d634c32", "reference": "c2f977551f0736d60467b3d754b2e0cf4e337b3f",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"phar-io/version": "^3.2",
"php": "^7.4 || ^8.0", "php": "^7.4 || ^8.0",
"phpstan/phpstan": "^2.1.32" "phpstan/phpstan": "^2.2.3"
}, },
"conflict": { "conflict": {
"phpunit/phpunit": "<7.0" "phpunit/phpunit": "<7.0"
@@ -9841,7 +9842,8 @@
"php-parallel-lint/php-parallel-lint": "^1.2", "php-parallel-lint/php-parallel-lint": "^1.2",
"phpstan/phpstan-deprecation-rules": "^2.0", "phpstan/phpstan-deprecation-rules": "^2.0",
"phpstan/phpstan-strict-rules": "^2.0", "phpstan/phpstan-strict-rules": "^2.0",
"phpunit/phpunit": "^9.6" "phpunit/phpunit": "^9.6",
"shipmonk/name-collision-detector": "^2.1"
}, },
"type": "phpstan-extension", "type": "phpstan-extension",
"extra": { "extra": {
@@ -9867,9 +9869,9 @@
], ],
"support": { "support": {
"issues": "https://github.com/phpstan/phpstan-phpunit/issues", "issues": "https://github.com/phpstan/phpstan-phpunit/issues",
"source": "https://github.com/phpstan/phpstan-phpunit/tree/2.0.16" "source": "https://github.com/phpstan/phpstan-phpunit/tree/2.0.17"
}, },
"time": "2026-02-14T09:05:21+00:00" "time": "2026-06-29T05:32:23+00:00"
}, },
{ {
"name": "phpstan/phpstan-symfony", "name": "phpstan/phpstan-symfony",
@@ -10330,16 +10332,16 @@
}, },
{ {
"name": "phpunit/phpunit", "name": "phpunit/phpunit",
"version": "13.2.1", "version": "13.2.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git", "url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "60da0ff1e10a0f72ee18a24117ec3b613a346bba" "reference": "492c067e618de7b3c76105082c90f9d2833401b7"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/60da0ff1e10a0f72ee18a24117ec3b613a346bba", "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/492c067e618de7b3c76105082c90f9d2833401b7",
"reference": "60da0ff1e10a0f72ee18a24117ec3b613a346bba", "reference": "492c067e618de7b3c76105082c90f9d2833401b7",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -10410,7 +10412,7 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues", "issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy", "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/13.2.1" "source": "https://github.com/sebastianbergmann/phpunit/tree/13.2.2"
}, },
"funding": [ "funding": [
{ {
@@ -10418,7 +10420,7 @@
"type": "other" "type": "other"
} }
], ],
"time": "2026-06-15T13:14:22+00:00" "time": "2026-06-29T13:36:29+00:00"
}, },
{ {
"name": "react/cache", "name": "react/cache",
@@ -10948,16 +10950,16 @@
}, },
{ {
"name": "rector/rector", "name": "rector/rector",
"version": "2.4.6", "version": "2.5.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/rectorphp/rector.git", "url": "https://github.com/rectorphp/rector.git",
"reference": "9b9e5c76618e4d359f65b54ca2eabcad3d1761ee" "reference": "49ff6339174bdbdf50b0b35ecbcff14a05ac9e24"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/rectorphp/rector/zipball/9b9e5c76618e4d359f65b54ca2eabcad3d1761ee", "url": "https://api.github.com/repos/rectorphp/rector/zipball/49ff6339174bdbdf50b0b35ecbcff14a05ac9e24",
"reference": "9b9e5c76618e4d359f65b54ca2eabcad3d1761ee", "reference": "49ff6339174bdbdf50b0b35ecbcff14a05ac9e24",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -10996,7 +10998,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/rectorphp/rector/issues", "issues": "https://github.com/rectorphp/rector/issues",
"source": "https://github.com/rectorphp/rector/tree/2.4.6" "source": "https://github.com/rectorphp/rector/tree/2.5.2"
}, },
"funding": [ "funding": [
{ {
@@ -11004,7 +11006,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2026-06-17T11:56:28+00:00" "time": "2026-06-22T11:39:33+00:00"
}, },
{ {
"name": "sebastian/cli-parser", "name": "sebastian/cli-parser",
@@ -12167,16 +12169,16 @@
}, },
{ {
"name": "symfony/browser-kit", "name": "symfony/browser-kit",
"version": "v8.1.0", "version": "v8.1.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/browser-kit.git", "url": "https://github.com/symfony/browser-kit.git",
"reference": "74e18e582cdda0eca35f7c74e1e48e62f0ede853" "reference": "f2ac86001ca9f487e8c6d0e11c8e33e6a9b8b2d5"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/browser-kit/zipball/74e18e582cdda0eca35f7c74e1e48e62f0ede853", "url": "https://api.github.com/repos/symfony/browser-kit/zipball/f2ac86001ca9f487e8c6d0e11c8e33e6a9b8b2d5",
"reference": "74e18e582cdda0eca35f7c74e1e48e62f0ede853", "reference": "f2ac86001ca9f487e8c6d0e11c8e33e6a9b8b2d5",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -12215,7 +12217,7 @@
"description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically", "description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"support": { "support": {
"source": "https://github.com/symfony/browser-kit/tree/v8.1.0" "source": "https://github.com/symfony/browser-kit/tree/v8.1.1"
}, },
"funding": [ "funding": [
{ {
@@ -12235,7 +12237,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2026-05-29T05:06:50+00:00" "time": "2026-06-09T10:54:51+00:00"
}, },
{ {
"name": "symfony/css-selector", "name": "symfony/css-selector",
@@ -12308,16 +12310,16 @@
}, },
{ {
"name": "symfony/dom-crawler", "name": "symfony/dom-crawler",
"version": "v8.1.0", "version": "v8.1.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/dom-crawler.git", "url": "https://github.com/symfony/dom-crawler.git",
"reference": "77ca351474ea018daba5f2e473cbf1b9b8e72ac6" "reference": "1dfadd25537c8fcb6752cce5775f24647d976bdc"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/dom-crawler/zipball/77ca351474ea018daba5f2e473cbf1b9b8e72ac6", "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/1dfadd25537c8fcb6752cce5775f24647d976bdc",
"reference": "77ca351474ea018daba5f2e473cbf1b9b8e72ac6", "reference": "1dfadd25537c8fcb6752cce5775f24647d976bdc",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -12354,7 +12356,7 @@
"description": "Eases DOM navigation for HTML and XML documents", "description": "Eases DOM navigation for HTML and XML documents",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"support": { "support": {
"source": "https://github.com/symfony/dom-crawler/tree/v8.1.0" "source": "https://github.com/symfony/dom-crawler/tree/v8.1.1"
}, },
"funding": [ "funding": [
{ {
@@ -12374,7 +12376,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2026-05-29T05:06:50+00:00" "time": "2026-06-05T06:23:12+00:00"
}, },
{ {
"name": "symfony/maker-bundle", "name": "symfony/maker-bundle",
@@ -12477,16 +12479,16 @@
}, },
{ {
"name": "symfony/phpunit-bridge", "name": "symfony/phpunit-bridge",
"version": "v8.1.0", "version": "v8.1.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/phpunit-bridge.git", "url": "https://github.com/symfony/phpunit-bridge.git",
"reference": "1fed488f8033f2dece371e60a1c66f2add274916" "reference": "3e1c9a9167e07474ec115555b632f0ffadb0f94d"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/1fed488f8033f2dece371e60a1c66f2add274916", "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/3e1c9a9167e07474ec115555b632f0ffadb0f94d",
"reference": "1fed488f8033f2dece371e60a1c66f2add274916", "reference": "3e1c9a9167e07474ec115555b632f0ffadb0f94d",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -12538,7 +12540,7 @@
"testing" "testing"
], ],
"support": { "support": {
"source": "https://github.com/symfony/phpunit-bridge/tree/v8.1.0" "source": "https://github.com/symfony/phpunit-bridge/tree/v8.1.1"
}, },
"funding": [ "funding": [
{ {
@@ -12558,20 +12560,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2026-05-29T05:06:50+00:00" "time": "2026-06-09T10:54:51+00:00"
}, },
{ {
"name": "symfony/web-profiler-bundle", "name": "symfony/web-profiler-bundle",
"version": "v8.1.0", "version": "v8.1.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/web-profiler-bundle.git", "url": "https://github.com/symfony/web-profiler-bundle.git",
"reference": "f8ccea08797a511b85a698b0da40e1b9e6461086" "reference": "eb4cf71d8fc496d790ec85b1b684a7ac30d57a96"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/f8ccea08797a511b85a698b0da40e1b9e6461086", "url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/eb4cf71d8fc496d790ec85b1b684a7ac30d57a96",
"reference": "f8ccea08797a511b85a698b0da40e1b9e6461086", "reference": "eb4cf71d8fc496d790ec85b1b684a7ac30d57a96",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -12623,7 +12625,7 @@
"dev" "dev"
], ],
"support": { "support": {
"source": "https://github.com/symfony/web-profiler-bundle/tree/v8.1.0" "source": "https://github.com/symfony/web-profiler-bundle/tree/v8.1.1"
}, },
"funding": [ "funding": [
{ {
@@ -12643,27 +12645,27 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2026-05-29T05:06:50+00:00" "time": "2026-06-05T06:23:12+00:00"
}, },
{ {
"name": "thecodingmachine/phpstan-safe-rule", "name": "thecodingmachine/phpstan-safe-rule",
"version": "v1.4.3", "version": "v1.4.7",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/thecodingmachine/phpstan-safe-rule.git", "url": "https://github.com/thecodingmachine/phpstan-safe-rule.git",
"reference": "5c804889253ce9498ef185e108e9f94b6023208e" "reference": "51fa2a35a270f683fc9ea53384a03e892b4d7b51"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/thecodingmachine/phpstan-safe-rule/zipball/5c804889253ce9498ef185e108e9f94b6023208e", "url": "https://api.github.com/repos/thecodingmachine/phpstan-safe-rule/zipball/51fa2a35a270f683fc9ea53384a03e892b4d7b51",
"reference": "5c804889253ce9498ef185e108e9f94b6023208e", "reference": "51fa2a35a270f683fc9ea53384a03e892b4d7b51",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"nikic/php-parser": "^5", "nikic/php-parser": "^5",
"php": "^8.1", "php": "^8.1",
"phpstan/phpstan": "^2.1.11", "phpstan/phpstan": "^2.2.2",
"thecodingmachine/safe": "^1.2 || ^2.0 || ^3.0" "thecodingmachine/safe": "^3.1"
}, },
"require-dev": { "require-dev": {
"php-coveralls/php-coveralls": "^2.1", "php-coveralls/php-coveralls": "^2.1",
@@ -12699,9 +12701,9 @@
"description": "A PHPStan rule to detect safety issues. Must be used in conjunction with thecodingmachine/safe", "description": "A PHPStan rule to detect safety issues. Must be used in conjunction with thecodingmachine/safe",
"support": { "support": {
"issues": "https://github.com/thecodingmachine/phpstan-safe-rule/issues", "issues": "https://github.com/thecodingmachine/phpstan-safe-rule/issues",
"source": "https://github.com/thecodingmachine/phpstan-safe-rule/tree/v1.4.3" "source": "https://github.com/thecodingmachine/phpstan-safe-rule/tree/v1.4.7"
}, },
"time": "2025-11-21T09:41:49+00:00" "time": "2026-06-21T07:55:55+00:00"
}, },
{ {
"name": "theseer/tokenizer", "name": "theseer/tokenizer",
@@ -12755,16 +12757,16 @@
}, },
{ {
"name": "vincentlanglet/twig-cs-fixer", "name": "vincentlanglet/twig-cs-fixer",
"version": "4.0.1", "version": "4.0.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/VincentLanglet/Twig-CS-Fixer.git", "url": "https://github.com/VincentLanglet/Twig-CS-Fixer.git",
"reference": "366f7cca494a6f95c5f410ae542aef9c164d329e" "reference": "1cb75618f7dd0f9bf51924aa6d3aa8c588f51d5a"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/VincentLanglet/Twig-CS-Fixer/zipball/366f7cca494a6f95c5f410ae542aef9c164d329e", "url": "https://api.github.com/repos/VincentLanglet/Twig-CS-Fixer/zipball/1cb75618f7dd0f9bf51924aa6d3aa8c588f51d5a",
"reference": "366f7cca494a6f95c5f410ae542aef9c164d329e", "reference": "1cb75618f7dd0f9bf51924aa6d3aa8c588f51d5a",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -12820,7 +12822,7 @@
"homepage": "https://github.com/VincentLanglet/Twig-CS-Fixer", "homepage": "https://github.com/VincentLanglet/Twig-CS-Fixer",
"support": { "support": {
"issues": "https://github.com/VincentLanglet/Twig-CS-Fixer/issues", "issues": "https://github.com/VincentLanglet/Twig-CS-Fixer/issues",
"source": "https://github.com/VincentLanglet/Twig-CS-Fixer/tree/4.0.1" "source": "https://github.com/VincentLanglet/Twig-CS-Fixer/tree/4.0.2"
}, },
"funding": [ "funding": [
{ {
@@ -12828,7 +12830,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2026-06-18T15:31:27+00:00" "time": "2026-06-29T15:22:14+00:00"
} }
], ],
"aliases": [], "aliases": [],
+16
View File
@@ -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 %}
+63
View File
@@ -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),
);
}
}