Skip to content

build(deps): bump the symfony group across 1 directory with 32 updates #6776

build(deps): bump the symfony group across 1 directory with 32 updates

build(deps): bump the symfony group across 1 directory with 32 updates #6776

Workflow file for this run

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ** Dynamic analysis **
#
# Dynamic analysis requires the Share community platform to run during evaluation. Currently Catroweb's dynamic tests
# are mainly UI-tests and a few unit tests. Hence, the tests take multiple hours. To reduce the run time, the tests
# are executed in multiple independent jobs. However, all jobs use the same Catroweb Docker image to reduce build time.
#
# Fork PR support:
# PRs from forks cannot push to GHCR (the GITHUB_TOKEN is read-only for the upstream org's packages).
# For fork PRs the build job exports the image as a tarball artifact instead. Downstream jobs detect which
# delivery method was used and pull/load accordingly. Internal PRs continue to use the fast GHCR path.
#
name: 'Dynamic analysis'
permissions:
contents: read
packages: write
# Run-on every creation, update and merge of a pull request.
# However, prevent checks to be initiated twice if the pull request is a branch on the same repository. (E.g dependabot)
on:
pull_request:
branches:
- '**' # all branches
schedule:
- cron: '0 4 * * *' # Every day at 04:00 UTC
workflow_dispatch: # Allow manual trigger
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: true
env:
REGISTRY: ghcr.io
IMAGE_NAME: catrobat/catroweb-test
jobs:
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Build:
#
# - In order to save computation time the "app.catroweb" image is only build once during this build phase.
# Other jobs can re-use this image to reduce their build time. With several jobs + the matrix build total
# computation time for this workflow can be highly reduced. This is important since we do not have unlimited
# resources/machines to run the jobs.
#
# - For internal PRs the image is pushed to GHCR. GHCR pulls are fast since GitHub runners have direct
# access to the registry.
#
# - For fork PRs the image is exported as a tarball and uploaded as a GitHub Actions artifact, because
# fork PR tokens lack write access to the upstream org's package registry.
#
build:
name: Build Catroweb Image
runs-on: ubuntu-latest
outputs:
image-tag: ${{ steps.meta.outputs.tags }}
is-fork: ${{ steps.fork-check.outputs.is_fork }}
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Check if fork PR
id: fork-check
run: |
if [ "${{ github.event_name }}" = "pull_request" ] && [ "${{ github.event.pull_request.head.repo.full_name }}" != "${{ github.repository }}" ]; then
echo "is_fork=true" >> "$GITHUB_OUTPUT"
else
echo "is_fork=false" >> "$GITHUB_OUTPUT"
fi
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Log in to GitHub Container Registry
if: steps.fork-check.outputs.is_fork == 'false'
uses: docker/login-action@v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Generate image metadata
id: meta
run: |
TAG="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.event.pull_request.head.sha || github.sha }}"
echo "tags=${TAG}" >> "$GITHUB_OUTPUT"
- name: Build and push Catroweb App Image (internal)
if: steps.fork-check.outputs.is_fork == 'false'
uses: docker/build-push-action@v7
with:
context: .
file: docker/Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
build-args: APP_ENVIRONMENT=test
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Build Catroweb App Image (fork)
if: steps.fork-check.outputs.is_fork == 'true'
uses: docker/build-push-action@v7
with:
context: .
file: docker/Dockerfile
load: true
tags: ${{ steps.meta.outputs.tags }}
build-args: APP_ENVIRONMENT=test
cache-from: type=gha
- name: Export image as tarball (fork)
if: steps.fork-check.outputs.is_fork == 'true'
run: docker save ${{ steps.meta.outputs.tags }} | gzip > /tmp/catroweb-image.tar.gz
- name: Upload image artifact (fork)
if: steps.fork-check.outputs.is_fork == 'true'
uses: actions/upload-artifact@v7
with:
name: catroweb-image
path: /tmp/catroweb-image.tar.gz
retention-days: 1
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# PhpUnit:
#
# - the tests are executed in our docker since we have many integration tests which access the database, etc.
# One might consider to strictly separate integration and unit tests. Units tests could be executed using
# composer scripts only to reduce the runtime to a few seconds. No build needed + dependencies can be easy cached.
#
# - A code coverage report is pushed to the artifacts where it can be downloaded directly on GitHub.
# Keep in mind the report is not including the tests written for behat.
#
tests_phpunit:
name: PHPUnit
needs: build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Log in to GitHub Container Registry
if: needs.build.outputs.is-fork == 'false'
uses: docker/login-action@v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Pull Docker Image (internal)
if: needs.build.outputs.is-fork == 'false'
run: docker pull ${{ needs.build.outputs.image-tag }}
- name: Download image artifact (fork)
if: needs.build.outputs.is-fork == 'true'
uses: actions/download-artifact@v8
with:
name: catroweb-image
path: /tmp
- name: Load Docker Image (fork)
if: needs.build.outputs.is-fork == 'true'
run: gunzip -c /tmp/catroweb-image.tar.gz | docker load
- name: Tag image for docker-compose
run: docker tag ${{ needs.build.outputs.image-tag }} app.catroweb
- name: Build and Start Docker Containers
run: |
cd docker
docker compose -f docker-compose.test.yaml up -d
- name: Run PHPUnit Tests
run: |
docker exec app.catroweb bin/phpunit --coverage-html tests/TestReports/CoverageReports/PhpUnit \
--coverage-clover=tests/TestReports/CoverageReports/PhpUnit/coverage.xml
- name: Upload PHPUnit Code Coverage Report
uses: actions/upload-artifact@v7
if: always()
with:
name: PhpUnitTestReport
path: tests/TestReports/CoverageReports/PhpUnit
- name: Upload Coverage to Codecov
uses: codecov/codecov-action@v6
with:
files: tests/TestReports/CoverageReports/PhpUnit/coverage.xml
flags: phpunit # optional
name: codecov-umbrella # optional
token: ${{ secrets.CODECOV_TOKEN }}
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Behat:
#
# - This job runs all Behat suites parallel using a matrix strategy. This is done since integrations tests which
# are interacting with a browser and the GUI are extremely slow. With a total run-time of over an hour, using this
# approach the run-time can be drastically reduced. The disadvantage, we can't easily create a coverage report
# for the behat scenarios. Something that is considered to be a bad practice anyway since Behat is intended to
# deliver scenario automation in BDD.
#
# - Behat and especially UI tests using Mink tend to flaky.
# A test will only be marked as failed if a test fails more than 3 times in a row.
# Flaky tests should be reduced to a minimum in the codebase!
#
# - Behat only reruns failed tests - Not pending/missing tests or those with exceptions!
# A pending/missing test will NOT result in a failed pipeline!
# This is the reason why the explicit check for the log file had to be added.
#
# - To ease the debugging, besides a log file, screenshots of failing tests are uploaded as artifacts.
#
# Notes:
# - Check the behat.yaml when changing / creating new suites
# - suites will finish their work even if another suite fails (fail-fast: false)
#
tests_behat:
name: Behat
needs: build
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
testSuite:
# --- API suites ---
- api-achievements api-notifications api-media-library api-studio # ~10m combined
- api-authentication api-comments api-followers api-sanitizer api-utility # ~8m combined
- api-moderation
- api-projects-get # split from api-projects (~10m)
- api-projects-write # split from api-projects (~10m)
- api-search api-translation
- api-user
# --- Web suites ---
- web-achievements
- web-admin-1 # split: projects_overview, featured_programs, approve_projects, login, moderation, maintenance (~7m)
- web-admin-2 # split: db_updater, example, feature_flag, mail, media_library, survey, upload, users (~7m)
- web-authentication web-media-library # ~8m combined
- web-code web-project web-scratch-integration web-system web-recommendations # ~8m combined
- web-comments web-reports # ~9m combined
- web-general web-notifications # ~10m combined
- web-profile-1 # split: profile_edit, profile, profile_image, data_export (~9m)
- web-profile-2 # split: follow, user_projects, verification, suspended, many_follower (~9m)
- web-project-details
- web-project-list web-remix-system web-reactions
- web-search web-top-bar
- web-studio
- web-translation
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Log in to GitHub Container Registry
if: needs.build.outputs.is-fork == 'false'
uses: docker/login-action@v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Pull Docker Image (internal)
if: needs.build.outputs.is-fork == 'false'
run: docker pull ${{ needs.build.outputs.image-tag }}
- name: Download image artifact (fork)
if: needs.build.outputs.is-fork == 'true'
uses: actions/download-artifact@v8
with:
name: catroweb-image
path: /tmp
- name: Load Docker Image (fork)
if: needs.build.outputs.is-fork == 'true'
run: gunzip -c /tmp/catroweb-image.tar.gz | docker load
- name: Tag image for docker-compose
run: docker tag ${{ needs.build.outputs.image-tag }} app.catroweb
- name: Build and Start Docker Containers
run: |
cd docker
docker compose -f docker-compose.test.yaml up -d
- name: Warmup the cache
run: |
docker exec app.catroweb bin/console cache:warmup -e test
# Derive a slug for log file names (replace spaces with underscores)
- name: Prepare suite variables
id: suite-vars
run: |
SLUG=$(echo "${{ matrix.testSuite }}" | tr ' ' '_')
echo "slug=$SLUG" >> "$GITHUB_OUTPUT"
# Test Run
# Matrix entries may contain multiple space-separated suites that run sequentially.
- name: Behat ${{ matrix.testSuite }} tests
id: test-run
continue-on-error: true
# - The output will of the tests will be piped to the stdout and into the log file.
# - A return code != 0 stops the execution of further commands in the pipeline.
# "tee" always returns 0, even if the behat test fails. Therefore, we need to exit with the first entry of
# the pipe status, which contains the correct exit code.
run: |
docker exec app.catroweb chmod -R 777 var
OVERALL_EXIT=0
for suite in ${{ matrix.testSuite }}; do
docker exec app.catroweb bin/behat -s "$suite" -f pretty --xdebug \
|& tee -a tests/TestReports/Behat/${{ steps.suite-vars.outputs.slug }}.log; \
EXIT_CODE=${PIPESTATUS[0]}
if [ "$EXIT_CODE" -ne 0 ]; then
OVERALL_EXIT=$EXIT_CODE
fi
done
echo "test-run-exit-code=$OVERALL_EXIT" > $GITHUB_ENV
( exit $OVERALL_EXIT )
# Missing steps are not rerun by behat, without this step they will be lost in the process
# We must explicitly kill the pipeline if the log contains undefined steps
- name: Check that suite has NO missing steps
if: always()
id: missing-check
run: |
if grep -q 'has missing steps. Define them with these snippets:' tests/TestReports/Behat/${{ steps.suite-vars.outputs.slug }}.log; then
cat tests/TestReports/Behat/${{ steps.suite-vars.outputs.slug }}.log
exit 1
fi
# Pending steps are not rerun by behat, without this step they will be lost in the process
# We must explicitly kill the pipeline if the log contains pending steps
- name: Check that suite has NO pending steps
if: always()
id: pending-check
run: |
if grep -q 'pending)' tests/TestReports/Behat/${{ steps.suite-vars.outputs.slug }}.log; then
cat tests/TestReports/Behat/${{ steps.suite-vars.outputs.slug }}.log
exit 1
fi
# Chrome exception are problems that can't be fixed with a rerun. However, we can try to run the whole suite one more time
- name: Check for Chrome Exceptions
if: always()
id: chrome-exception-check
run: |
if grep -q '\[DMore\\ChromeDriver\\StreamReadException\]' tests/TestReports/Behat/${{ steps.suite-vars.outputs.slug }}.log; then
cat tests/TestReports/Behat/${{ steps.suite-vars.outputs.slug }}.log
for suite in ${{ matrix.testSuite }}; do
docker exec app.catroweb bin/behat -s "$suite" -f pretty
done
fi
- name: Upload Behat Test Artifacts
uses: actions/upload-artifact@v7
with:
name: logs_${{ steps.suite-vars.outputs.slug }}
path: |
tests/TestReports/Behat/${{ steps.suite-vars.outputs.slug }}.log
tests/TestReports/TestScreenshots
- name: Upload Coverage to Codecov
uses: codecov/codecov-action@v6
with:
files: tests/TestReports/CoverageReports/Behat/coverage.xml
flags: behat # optional
name: codecov-umbrella # optional
token: ${{ secrets.CODECOV_TOKEN }}
- name: Rerun Behat Tests (1st Attempt)
if: env.test-run-exit-code != '0'
id: test-rerun-1
continue-on-error: true
run: |
docker exec app.catroweb chmod -R 777 var
OVERALL_EXIT=0
for suite in ${{ matrix.testSuite }}; do
docker exec app.catroweb bin/behat -s "$suite" --rerun
EXIT_CODE=$?
if [ "$EXIT_CODE" -ne 0 ]; then OVERALL_EXIT=$EXIT_CODE; fi
done
echo "test-run-exit-code=$OVERALL_EXIT" > $GITHUB_ENV
( exit $OVERALL_EXIT )
- name: Rerun Behat Tests (2nd Attempt)
if: env.test-run-exit-code != '0'
id: test-rerun-2
continue-on-error: true
run: |
OVERALL_EXIT=0
for suite in ${{ matrix.testSuite }}; do
docker exec app.catroweb bin/behat -s "$suite" --rerun
EXIT_CODE=$?
if [ "$EXIT_CODE" -ne 0 ]; then OVERALL_EXIT=$EXIT_CODE; fi
done
echo "test-run-exit-code=$OVERALL_EXIT" > $GITHUB_ENV
( exit $OVERALL_EXIT )
- name: Rerun Behat Tests (3rd Attempt)
if: env.test-run-exit-code != '0'
id: test-rerun-3
run: |
for suite in ${{ matrix.testSuite }}; do
docker exec app.catroweb bin/behat -f pretty -s "$suite" --rerun || exit 1
done
## Failure debugging
- name: Debug Failure
if: failure()
run: |
docker ps -a
echo "--- App Logs ---"
docker logs app.catroweb
echo "--- DB Logs ---"
docker logs db.catroweb.test
echo "--- Chrome Logs ---"
docker logs chrome.catroweb