name: Tests on: push: branches: [ main, develop, feature/* ] pull_request: branches: [ main, develop ] jobs: setup-base: name: Build and Push CICD Base Image runs-on: ubuntu-act steps: - name: Minimal checkout for base Dockerfile env: SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} run: | echo "=== Minimal Repository Checkout for Base Dockerfile ===" # Set up SSH key securely (temporary file approach) if [ -n "${SSH_PRIVATE_KEY}" ]; then mkdir -p ~/.ssh echo "${SSH_PRIVATE_KEY}" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa ssh-keyscan -p 2222 dogar.darkhelm.org >> ~/.ssh/known_hosts 2>/dev/null fi # Clone just enough to get the Dockerfile GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=no" \ git clone --depth 1 --no-checkout \ ssh://git@dogar.darkhelm.org:2222/DarkHelm.org/plex-playlist.git . # Checkout only the base Dockerfile and dockerignore git checkout HEAD -- Dockerfile.cicd-base .dockerignore # Clean up SSH key for security rm -f ~/.ssh/id_rsa echo "✓ Dockerfile.cicd-base ready for build" - name: Check if base image needs rebuilding id: check-base env: PACKAGE_ACCESS_TOKEN: ${{ secrets.PACKAGE_ACCESS_TOKEN }} REGISTRY_USER: ${{ secrets.REGISTRY_USER || github.actor }} run: | echo "=== Checking if CICD Base Image Needs Rebuilding ===" # Login to registry to check for existing image if echo "${PACKAGE_ACCESS_TOKEN}" | docker login dogar.darkhelm.org -u "${REGISTRY_USER}" --password-stdin; then echo "✓ Successfully logged into registry" else echo "❌ Failed to login to registry, will force build" echo "needs_build=true" >> $GITHUB_OUTPUT exit 0 fi # Calculate hash of base Dockerfile for cache key BASE_HASH=$(sha256sum Dockerfile.cicd-base | cut -d' ' -f1 | head -c16) echo "Base Dockerfile hash: ${BASE_HASH}" echo "base_hash=${BASE_HASH}" >> $GITHUB_OUTPUT # Try to pull existing base image with this hash (with timeout handling) echo "Attempting to pull base image with hash: ${BASE_HASH}" PULL_SUCCESS=false # First try to pull the hash-specific image if timeout 120 docker pull dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd-base:${BASE_HASH} 2>/dev/null; then echo "✓ Base image with hash ${BASE_HASH} found in registry" PULL_SUCCESS=true else echo "Base image with hash ${BASE_HASH} not found, trying latest..." # Fallback: try to pull latest and check if it matches our hash if timeout 120 docker pull dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd-base:latest 2>/dev/null; then # Check if the pulled latest image is what we need (this is a simplified check) echo "Pulled latest base image, will use it for now" docker tag dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd-base:latest \ dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd-base:${BASE_HASH} PULL_SUCCESS=true fi fi if [ "$PULL_SUCCESS" = true ]; then echo "✓ Base image available, skipping build" echo "needs_build=false" >> $GITHUB_OUTPUT # Ensure latest tag is available for dependent job docker tag dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd-base:${BASE_HASH} \ dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd-base:latest 2>/dev/null || true docker push dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd-base:latest 2>/dev/null || \ echo "Warning: Could not push latest tag, but base image is available" else echo "Base image not available in registry - will build new image" echo "This is normal for first run or when base dependencies change" echo "needs_build=true" >> $GITHUB_OUTPUT fi - name: Build and push base image if: steps.check-base.outputs.needs_build == 'true' env: PACKAGE_ACCESS_TOKEN: ${{ secrets.PACKAGE_ACCESS_TOKEN }} REGISTRY_USER: ${{ secrets.REGISTRY_USER || github.actor }} BASE_HASH: ${{ steps.check-base.outputs.base_hash }} run: | echo "=== Building CICD Base Image ===" echo "Base hash: ${BASE_HASH}" # Enable Docker BuildKit export DOCKER_BUILDKIT=1 # Build base image (no secrets needed for base dependencies) echo "Building base image..." if docker build -f Dockerfile.cicd-base \ --build-arg BASE_IMAGE_VERSION="v1.0.0-${BASE_HASH}" \ -t cicd-base:latest .; then echo "✓ Base image built successfully" else echo "❌ Failed to build base image" exit 1 fi # Tag for registry with hash and latest docker tag cicd-base:latest dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd-base:${BASE_HASH} docker tag cicd-base:latest dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd-base:latest # Push to registry with retry echo "Pushing base images to registry..." for i in 1 2 3; do echo "Push attempt $i..." if docker push dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd-base:${BASE_HASH} && \ docker push dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd-base:latest; then echo "✓ CICD base image built and pushed with hash ${BASE_HASH}" break else echo "❌ Push attempt $i failed" if [ $i -eq 3 ]; then echo "❌ All push attempts failed, but base image is built locally" echo "The complete image job can use the local base image" else sleep 10 fi fi done setup: name: Build and Push CICD Complete Image runs-on: ubuntu-act needs: setup-base steps: - name: Minimal checkout for Dockerfile env: SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} run: | echo "=== Minimal Repository Checkout for Complete Dockerfile ===" # Set up SSH key securely (temporary file approach) if [ -n "${SSH_PRIVATE_KEY}" ]; then mkdir -p ~/.ssh echo "${SSH_PRIVATE_KEY}" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa ssh-keyscan -p 2222 dogar.darkhelm.org >> ~/.ssh/known_hosts 2>/dev/null fi # Clone just enough to get the Dockerfiles GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=no" \ git clone --depth 1 --no-checkout \ ssh://git@dogar.darkhelm.org:2222/DarkHelm.org/plex-playlist.git . # Checkout Dockerfiles and dockerignore (include base for fallback) git checkout HEAD -- Dockerfile.cicd Dockerfile.cicd-base .dockerignore # Clean up SSH key for security rm -f ~/.ssh/id_rsa echo "✓ Dockerfile.cicd and fallback base ready for secure build" - name: Build and push complete CICD image env: PACKAGE_ACCESS_TOKEN: ${{ secrets.PACKAGE_ACCESS_TOKEN }} SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} GITHUB_SHA: ${{ github.sha }} REGISTRY_USER: ${{ secrets.REGISTRY_USER || github.actor }} run: | echo "=== Building Complete CICD Image with Secure Secrets ===" # Login to registry echo "${PACKAGE_ACCESS_TOKEN}" | docker login dogar.darkhelm.org -u "${REGISTRY_USER}" --password-stdin # Verify base image availability with fallback strategy BASE_IMAGE="dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd-base:latest" echo "Checking base image availability: ${BASE_IMAGE}" if docker pull "${BASE_IMAGE}" 2>/dev/null; then echo "✓ Base image pulled successfully from registry" else echo "❌ Failed to pull base image from registry" echo "This might be the first run - checking if we need to build base locally..." # Check if base Dockerfile exists and build it locally as fallback if [ -f "Dockerfile.cicd-base" ]; then echo "Building base image locally as fallback..." export DOCKER_BUILDKIT=1 docker build -f Dockerfile.cicd-base -t cicd-base-local:latest . BASE_IMAGE="cicd-base-local:latest" echo "✓ Base image built locally: ${BASE_IMAGE}" else echo "❌ Cannot find Dockerfile.cicd-base for fallback build" exit 1 fi fi # Create temporary SSH key file for BuildKit secrets echo "${SSH_PRIVATE_KEY}" > /tmp/ssh_key chmod 600 /tmp/ssh_key # Enable Docker BuildKit for secrets support export DOCKER_BUILDKIT=1 # Build complete CICD image using secure BuildKit secrets, inheriting from base # SSH key is mounted securely and never stored in image layers echo "Building complete image with base: ${BASE_IMAGE}" docker build -f Dockerfile.cicd \ --secret id=ssh_private_key,src=/tmp/ssh_key \ --build-arg GITHUB_SHA="$GITHUB_SHA" \ --build-arg CICD_BASE_IMAGE="${BASE_IMAGE}" \ -t cicd:latest . # Clean up temporary SSH key file rm -f /tmp/ssh_key # Tag for Gitea container registry docker tag cicd:latest dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:latest docker tag cicd:latest dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} # Push to registry echo "Pushing complete CICD images to registry..." docker push dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:latest docker push dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} echo "✓ Complete CICD image built and pushed to registry" # Pre-commit style checks - General file formatting trailing-whitespace: name: Trailing Whitespace Check runs-on: ubuntu-act needs: setup steps: - name: Login to Gitea Container Registry run: echo "${{ secrets.PACKAGE_ACCESS_TOKEN }}" | docker login dogar.darkhelm.org -u ${{ github.actor }} --password-stdin - name: Check trailing whitespace with pre-commit run: | docker pull dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} docker run --rm dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} bash -c " cd /workspace && pre-commit run trailing-whitespace --all-files --show-diff-on-failure " end-of-file-fixer: name: End of File Check runs-on: ubuntu-act needs: setup steps: - name: Login to Gitea Container Registry run: echo "${{ secrets.PACKAGE_ACCESS_TOKEN }}" | docker login dogar.darkhelm.org -u ${{ github.actor }} --password-stdin - name: Check end of file with pre-commit run: | docker pull dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} docker run --rm dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} bash -c " cd /workspace && pre-commit run end-of-file-fixer --all-files --show-diff-on-failure " check-yaml: name: YAML Syntax Check runs-on: ubuntu-act needs: setup steps: - name: Login to Gitea Container Registry run: echo "${{ secrets.PACKAGE_ACCESS_TOKEN }}" | docker login dogar.darkhelm.org -u ${{ github.actor }} --password-stdin - name: Check YAML files with pre-commit run: | docker pull dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} docker run --rm dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} bash -c " cd /workspace && pre-commit run check-yaml --all-files --show-diff-on-failure " check-toml: name: TOML Syntax Check runs-on: ubuntu-act needs: setup steps: - name: Login to Gitea Container Registry run: echo "${{ secrets.PACKAGE_ACCESS_TOKEN }}" | docker login dogar.darkhelm.org -u ${{ github.actor }} --password-stdin - name: Check TOML files with pre-commit run: | docker pull dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} docker run --rm dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} bash -c " cd /workspace && pre-commit run check-toml --all-files --show-diff-on-failure " mixed-line-ending: name: Mixed Line Ending Check runs-on: ubuntu-act needs: setup steps: - name: Login to Gitea Container Registry run: echo "${{ secrets.PACKAGE_ACCESS_TOKEN }}" | docker login dogar.darkhelm.org -u ${{ github.actor }} --password-stdin - name: Check line endings with pre-commit run: | docker pull dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} docker run --rm dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} bash -c " cd /workspace && pre-commit run mixed-line-ending --all-files --show-diff-on-failure " toml-lint: name: TOML Formatting Check runs-on: ubuntu-act needs: setup steps: - name: Login to Gitea Container Registry run: | echo "=== Network-Resilient Docker Registry Login ===" for i in 1 2 3 4 5; do echo "Login attempt $i/5..." if echo "${{ secrets.PACKAGE_ACCESS_TOKEN }}" | timeout 60 docker login dogar.darkhelm.org -u ${{ github.actor }} --password-stdin; then echo "✓ Login successful" break else if [ $i -eq 5 ]; then echo "❌ All login attempts failed after network timeouts" exit 1 fi echo "⚠ Login attempt $i failed, waiting 15s before retry..." sleep 15 fi done - name: Check TOML formatting with pre-commit run: | echo "=== Network-Resilient Docker Operations ===" # Resilient docker pull with retries for i in 1 2 3; do echo "Docker pull attempt $i/3..." if timeout 300 docker pull dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest}; then echo "✓ Docker pull successful" break else if [ $i -eq 3 ]; then echo "❌ All docker pull attempts failed" exit 1 fi echo "⚠ Docker pull attempt $i failed, waiting 20s before retry..." sleep 20 fi done # Run the actual test docker run --rm dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} bash -c " cd /workspace && pre-commit run pretty-format-toml --all-files --show-diff-on-failure " # Backend Python checks ruff-lint: name: Ruff Linting runs-on: ubuntu-act needs: setup steps: - name: Login to Gitea Container Registry run: echo "${{ secrets.PACKAGE_ACCESS_TOKEN }}" | docker login dogar.darkhelm.org -u ${{ github.actor }} --password-stdin - name: Run ruff linting with pre-commit run: | docker pull dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} docker run --rm dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} bash -c " cd /workspace && pre-commit run ruff --all-files --show-diff-on-failure " ruff-format: name: Ruff Format Check runs-on: ubuntu-act needs: setup steps: - name: Login to Gitea Container Registry run: echo "${{ secrets.PACKAGE_ACCESS_TOKEN }}" | docker login dogar.darkhelm.org -u ${{ github.actor }} --password-stdin - name: Check ruff formatting with pre-commit run: | docker pull dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} docker run --rm dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} bash -c " cd /workspace && pre-commit run ruff-format --all-files --show-diff-on-failure " pyright: name: Pyright Type Check runs-on: ubuntu-act needs: setup steps: - name: Login to Gitea Container Registry run: echo "${{ secrets.PACKAGE_ACCESS_TOKEN }}" | docker login dogar.darkhelm.org -u ${{ github.actor }} --password-stdin - name: Run pyright type checking with pre-commit run: | docker pull dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} docker run --rm dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} bash -c " cd /workspace && pre-commit run pyright --all-files --show-diff-on-failure " darglint: name: Darglint Docstring Check runs-on: ubuntu-act needs: setup steps: - name: Login to Gitea Container Registry run: echo "${{ secrets.PACKAGE_ACCESS_TOKEN }}" | docker login dogar.darkhelm.org -u ${{ github.actor }} --password-stdin - name: Run darglint docstring linting with pre-commit run: | docker pull dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} docker run --rm dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} bash -c " cd /workspace && pre-commit run darglint --all-files --show-diff-on-failure " no-docstring-types: name: No Docstring Types Check runs-on: ubuntu-act needs: setup steps: - name: Login to Gitea Container Registry run: echo "${{ secrets.PACKAGE_ACCESS_TOKEN }}" | docker login dogar.darkhelm.org -u ${{ github.actor }} --password-stdin - name: Run no docstring types check with pre-commit run: | docker pull dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} docker run --rm dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} bash -c " cd /workspace && pre-commit run no-docstring-types --all-files --show-diff-on-failure " # Frontend checks eslint: name: ESLint Check runs-on: ubuntu-act needs: setup steps: - name: Login to Gitea Container Registry run: echo "${{ secrets.PACKAGE_ACCESS_TOKEN }}" | docker login dogar.darkhelm.org -u ${{ github.actor }} --password-stdin - name: Run ESLint with pre-commit run: | docker pull dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} docker run --rm dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} bash -c " cd /workspace && pre-commit run eslint --all-files --show-diff-on-failure " prettier: name: Prettier Format Check runs-on: ubuntu-act needs: setup steps: - name: Login to Gitea Container Registry run: echo "${{ secrets.PACKAGE_ACCESS_TOKEN }}" | docker login dogar.darkhelm.org -u ${{ github.actor }} --password-stdin - name: Check Prettier formatting with pre-commit run: | docker pull dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} docker run --rm dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} bash -c " cd /workspace && pre-commit run prettier --all-files --show-diff-on-failure " typescript-check: name: TypeScript Type Check runs-on: ubuntu-act needs: setup steps: - name: Login to Gitea Container Registry run: echo "${{ secrets.PACKAGE_ACCESS_TOKEN }}" | docker login dogar.darkhelm.org -u ${{ github.actor }} --password-stdin - name: Run TypeScript type checking with pre-commit run: | docker pull dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} docker run --rm dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} bash -c " cd /workspace && pre-commit run typescript-check --all-files --show-diff-on-failure " tsdoc-lint: name: TSDoc Lint Check runs-on: ubuntu-act needs: setup steps: - name: Login to Gitea Container Registry run: echo "${{ secrets.PACKAGE_ACCESS_TOKEN }}" | docker login dogar.darkhelm.org -u ${{ github.actor }} --password-stdin - name: Run TSDoc linting with pre-commit run: | docker pull dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} docker run --rm dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} bash -c " cd /workspace && pre-commit run tsdoc-lint --all-files --show-diff-on-failure " # Unit tests with coverage backend-tests: name: Backend Tests runs-on: ubuntu-act needs: setup steps: - name: Login to Gitea Container Registry run: echo "${{ secrets.PACKAGE_ACCESS_TOKEN }}" | docker login dogar.darkhelm.org -u ${{ github.actor }} --password-stdin - name: Run backend tests with coverage run: | docker pull dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} docker run --rm dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} bash -c " cd /workspace/backend && source .venv/bin/activate && uv run pytest -v --tb=short --cov=src --cov-report=term-missing --cov-fail-under=95 " frontend-tests: name: Frontend Tests runs-on: ubuntu-act needs: setup steps: - name: Login to Gitea Container Registry run: echo "${{ secrets.PACKAGE_ACCESS_TOKEN }}" | docker login dogar.darkhelm.org -u ${{ github.actor }} --password-stdin - name: Run frontend tests with coverage run: | docker pull dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} docker run --rm dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} bash -c " cd /workspace/frontend && yarn test:coverage --run --reporter=verbose --coverage.reporter=text --coverage.reporter=text-summary --coverage.thresholds.lines=85 --coverage.thresholds.functions=85 --coverage.thresholds.branches=85 --coverage.thresholds.statements=85 " # Doctest for backend xdoctest: name: Backend Doctests runs-on: ubuntu-act needs: setup steps: - name: Login to Gitea Container Registry run: echo "${{ secrets.PACKAGE_ACCESS_TOKEN }}" | docker login dogar.darkhelm.org -u ${{ github.actor }} --password-stdin - name: Run backend doctests run: | docker pull dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} docker run --rm dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} bash -c " cd /workspace/backend && source .venv/bin/activate && echo 'Running doctests...' && if uv run xdoctest src/ --quiet; then echo '✓ All doctests passed' else echo 'ℹ No doctests found or some doctests failed' # Don't fail the build for missing doctests, only for failed ones if uv run xdoctest src/ --quiet --verbose 2>&1 | grep -q 'FAILED'; then exit 1 fi fi " # Integration and E2E tests (run after unit tests complete) integration-tests: name: Integration Tests runs-on: ubuntu-act needs: [backend-tests, frontend-tests] steps: - name: Login to Gitea Container Registry run: echo "${{ secrets.PACKAGE_ACCESS_TOKEN }}" | docker login dogar.darkhelm.org -u ${{ github.actor }} --password-stdin - name: Run integration tests run: | docker pull dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} docker run --rm dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} bash -c " cd /workspace/backend && source .venv/bin/activate && if [ -d 'tests/integration' ]; then uv run pytest tests/integration/ -v --tb=short else echo 'ℹ No integration tests found' fi " e2e-tests: name: End-to-End Tests runs-on: ubuntu-act needs: [backend-tests, frontend-tests] steps: - name: Login to Gitea Container Registry run: | echo "=== Network-Resilient Docker Registry Login ===" for i in 1 2 3 4 5; do echo "Login attempt $i/5..." if echo "${{ secrets.PACKAGE_ACCESS_TOKEN }}" | timeout 60 docker login dogar.darkhelm.org -u ${{ github.actor }} --password-stdin; then echo "✓ Login successful" break else if [ $i -eq 5 ]; then echo "❌ All login attempts failed after network timeouts" exit 1 fi echo "⚠ Login attempt $i failed, waiting 15s before retry..." sleep 15 fi done - name: Run E2E tests run: | echo "=== Network-Resilient E2E Test Execution ===" # Resilient docker pull for i in 1 2 3; do echo "Docker pull attempt $i/3..." if timeout 300 docker pull dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest}; then echo "✓ Docker pull successful" break else if [ $i -eq 3 ]; then echo "❌ All docker pull attempts failed" exit 1 fi echo "⚠ Docker pull attempt $i failed, waiting 20s before retry..." sleep 20 fi done # Run E2E tests with network resilience docker run --rm -e CI=true dogar.darkhelm.org/darkhelm.org/plex-playlist/cicd:${GITHUB_SHA:-latest} bash -c " cd /workspace/frontend && if [ -d 'tests/e2e' ] || grep -q 'playwright' package.json; then echo 'Running E2E tests with Playwright (Network Resilient)...' && export CI=true && export NODE_ENV=test && # Network-resilient Playwright setup echo 'Verifying Playwright installation...' && yarn playwright --version && echo 'Installing Playwright browser binaries with retries...' && for i in 1 2 3; do echo \"Browser install attempt \$i/3...\" && if timeout 600 yarn playwright install --with-deps; then echo \"✓ Playwright browsers installed successfully\" && break else if [ \$i -eq 3 ]; then echo \"❌ All browser install attempts failed\" && exit 1 fi echo \"⚠ Browser install attempt \$i failed, waiting 30s before retry...\" && sleep 30 fi done && echo 'Running E2E tests with network resilience...' && # Set additional network timeout environment variables export PLAYWRIGHT_TIMEOUT=90000 && export NODE_TLS_REJECT_UNAUTHORIZED=0 && yarn test:e2e --reporter=list --timeout=90000 else echo 'ℹ No E2E tests found' fi "