diff --git a/.github/files/docker-compose.yaml b/.github/files/docker-compose.yaml index 2e5ba8c..071023e 100644 --- a/.github/files/docker-compose.yaml +++ b/.github/files/docker-compose.yaml @@ -64,7 +64,11 @@ services: image: gotenberg/gotenberg:8 mercure: - image: dunglas/mercure + # Pinned to the 0.x protocol: Mercure 1.0 (2026-09-16) only accepts OAuth 2.0 access tokens + # (typ at+jwt, iss, aud, authorization_details) and rejects the legacy `mercure`-claim JWT + # Pimcore publishes with ("invalid JWT: untrusted issuer"), which leaves every + # execution-engine job stuck in "running". + image: dunglas/mercure:v0.24 restart: unless-stopped environment: SERVER_NAME: ':80' diff --git a/.github/scripts/02-install-pimcore.sh b/.github/scripts/02-install-pimcore.sh index 364ee0b..db41846 100755 --- a/.github/scripts/02-install-pimcore.sh +++ b/.github/scripts/02-install-pimcore.sh @@ -164,6 +164,12 @@ docker compose exec -T php bin/console generic-data-index:update:index -r 2>/dev echo " [sudo] Setting ownership to match container user (DOCKER_UID:DOCKER_GID)" sudo chown -R "${DOCKER_UID}:${DOCKER_GID}" . +# The supervisord container was started in step 1, before Pimcore was installed. Its long-running +# messenger:consume worker would otherwise keep whatever state it booted with (no schema, stale +# container cache), so restart it now that installation, cache:clear and chown are done. +echo ">>> Restarting supervisord so the messenger worker boots against the installed app..." +docker compose restart supervisord + echo "" echo ">>> Installation complete." echo " Pimcore UI: http://localhost:${NGINX_PORT}/pimcore-studio" diff --git a/.github/scripts/07-collect-logs.sh b/.github/scripts/07-collect-logs.sh new file mode 100755 index 0000000..a2bb752 --- /dev/null +++ b/.github/scripts/07-collect-logs.sh @@ -0,0 +1,70 @@ +#!/usr/bin/env bash +# +# 07-collect-logs.sh — Save container and Pimcore logs before shutdown +# +# Used by CI (on failure, before 04-shutdown.sh) and local. +# Writes everything to /container-logs/. +# +set -euo pipefail + +SCRIPTS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "${SCRIPTS_DIR}/../.." && pwd)" +FILES_DIR="${SCRIPTS_DIR}/../files" +PROJECT_PATH="${REPO_ROOT}/../test-project" +LOGS_DIR="${REPO_ROOT}/container-logs" + +if [[ ! -d "$PROJECT_PATH" ]]; then + echo ">>> No project directory found at ${PROJECT_PATH}, no logs to collect." + exit 0 +fi + +source "${FILES_DIR}/.env" +[[ -f "${SCRIPTS_DIR}/.env.local" ]] && source "${SCRIPTS_DIR}/.env.local" + +export NGINX_PORT OPENSEARCH_DASHBOARDS_PORT MAILPIT_PORT MERCURE_PORT DB_PORT +export PHP_IMAGE PHP_SUPERVISORD_IMAGE +export MYSQL_ROOT_PASSWORD MYSQL_DATABASE MYSQL_USER MYSQL_PASSWORD +export OPENSEARCH_INITIAL_ADMIN_PASSWORD +export DOCKER_UID="${DOCKER_UID:-$(id -u)}" +export DOCKER_GID="${DOCKER_GID:-$(id -g)}" + +mkdir -p "$LOGS_DIR" +cd "$PROJECT_PATH" + +echo ">>> Collecting container logs into ${LOGS_DIR}..." +docker compose ps --all > "${LOGS_DIR}/compose-ps.txt" 2>&1 || true +for service in $(docker compose config --services); do + docker compose logs --no-color --timestamps "$service" > "${LOGS_DIR}/${service}.log" 2>&1 || true +done + +echo ">>> Collecting Pimcore var/log..." +if [[ -d var/log ]]; then + cp -r var/log "${LOGS_DIR}/var-log" || true +fi + +# The artifact is downloadable from a public repository, so scrub the secret values the job +# exports before it is uploaded — GitHub only masks them in the step log, not in artifacts. +echo ">>> Redacting secrets..." +for secret_var in PIMCORE_PRODUCT_KEY PIMCORE_ENCRYPTION_SECRET PIMCORE_INSTANCE_IDENTIFIER COMPOSER_TOKEN; do + secret="${!secret_var:-}" + [[ -z "$secret" ]] && continue + # JSON log lines (Mercure, Monolog context) escape "/" as "\/", so scrub that form as well. + variants=("$secret") + json_escaped="${secret//\//\\/}" + [[ "$json_escaped" != "$secret" ]] && variants+=("$json_escaped") + for variant in "${variants[@]}"; do + SECRET="$variant" find "$LOGS_DIR" -type f -exec \ + perl -pi -e 's/\Q$ENV{SECRET}\E/[REDACTED]/g' {} + + # perl -i only warns and still exits 0 when it cannot open a file, so verify the result + # instead: grep exits 1 only if every file was read and none still holds the secret. + grep_status=0 + grep -rqF -- "$variant" "$LOGS_DIR" || grep_status=$? + if [[ "$grep_status" -ne 1 ]]; then + echo ">>> ${secret_var} could not be redacted from every file, refusing to keep the logs." >&2 + rm -rf "$LOGS_DIR" + exit 1 + fi + done +done + +echo ">>> Log collection complete." diff --git a/.github/workflows/api-tests.yml b/.github/workflows/api-tests.yml index d0be3b1..0f3238b 100644 --- a/.github/workflows/api-tests.yml +++ b/.github/workflows/api-tests.yml @@ -111,6 +111,22 @@ jobs: path: tests/test-results/ retention-days: 30 + - name: Collect container logs + id: collect-logs + if: failure() + env: + COMPOSER_TOKEN: ${{ secrets.COMPOSER_PIMCORE_REPO_PACKAGIST_TOKEN }} + run: bash .github/scripts/07-collect-logs.sh + + - name: Upload container logs + # The artifact is public: only upload once the collector has finished redacting. + if: failure() && steps.collect-logs.outcome == 'success' + uses: actions/upload-artifact@v7 + with: + name: container-logs-${{ matrix.platform }} + path: container-logs/ + retention-days: 30 + - name: Shutdown if: always() run: bash .github/scripts/04-shutdown.sh diff --git a/.gitignore b/.gitignore index 648dda5..be34ce0 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,6 @@ Thumbs.db /tests/playwright-report /tests/test-results /tests/.env + +# API test log dumps (.github/scripts/07-collect-logs.sh) +/container-logs/ diff --git a/doc/04_API_Testing/01_API_TESTS.md b/doc/04_API_Testing/01_API_TESTS.md index 6ed835f..339079f 100644 --- a/doc/04_API_Testing/01_API_TESTS.md +++ b/doc/04_API_Testing/01_API_TESTS.md @@ -68,8 +68,9 @@ Admin credentials: `admin` / `admin` ## Reset / shutdown ```bash -.github/scripts/05-reset.sh # fast DB reset, keeps containers -.github/scripts/06-teardown.sh # full shutdown +.github/scripts/05-reset.sh # fast DB reset, keeps containers +.github/scripts/06-teardown.sh # full shutdown +.github/scripts/07-collect-logs.sh # dump container logs + var/log to container-logs/ ``` ## Testing a Platform Version Before Its Release