name: CD - Release Build

# This workflow builds and publishes official releases
# 
# Testing Strategy:
# - Full test suite runs on PRs via ci-comprehensive.yml
# - This workflow focuses on building and publishing
# - Security audit still runs to catch any last-minute issues
# - Tests can optionally be run via workflow_dispatch for manual releases
#
# Workflow is triggered by:
# - Push to main/master (after PR merge from RC)
# - Git tags (v*.*.*)
# - Release events
# - Manual workflow_dispatch

on:
  push:
    branches: [ main, master ]
    tags: [ 'v*.*.*' ]
  release:
    types: [ published ]
  workflow_dispatch:
    inputs:
      version:
        description: 'Release version (e.g., v1.2.3) - must match version in setup.py'
        required: true
        type: string
      skip_tests:
        description: 'Skip tests (opt-in only; default is to run tests on manual release)'
        required: false
        type: boolean
        default: false

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}
  # Docker Hub repo to also publish release images to.
  # Requires GitHub Actions secrets:
  # - DOCKERHUB_USERNAME
  # - DOCKERHUB_TOKEN (recommended) or DOCKERHUB_PASSWORD
  DOCKERHUB_IMAGE: driesp/timetracker
  PYTHON_VERSION: '3.11'
  NODE_VERSION: '24'

jobs:
  # ============================================================================
  # Full Test Suite (Optional - tests already ran on PR)
  # ============================================================================
  full-test-suite:
    name: Full Test Suite (Optional)
    runs-on: ubuntu-latest
    # Skip by default since tests already ran on PR
    # Only run if explicitly requested via workflow_dispatch
    if: github.event_name == 'workflow_dispatch' && github.event.inputs.skip_tests != 'true'
    timeout-minutes: 30
    
    services:
      postgres:
        image: postgres:16-alpine
        env:
          POSTGRES_PASSWORD: test_password
          POSTGRES_USER: test_user
          POSTGRES_DB: test_db
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
        ports:
          - 5432:5432
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      
      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: ${{ env.PYTHON_VERSION }}
          cache: 'pip'
      
      - name: Install dependencies
        run: |
          pip install -r requirements.txt
          pip install -r requirements-test.txt
          pip install -e .
      
      - name: Validate database migrations
        env:
          DATABASE_URL: postgresql://test_user:test_password@localhost:5432/test_db
          FLASK_APP: app.py
          FLASK_ENV: testing
        run: |
          echo "🔍 Validating database migrations..."
          
          # Check if there are migration-related changes
          if git diff --name-only HEAD~1 2>/dev/null | grep -E "(app/models/|migrations/)" > /dev/null; then
            echo "📋 Migration-related changes detected"
            
            # Initialize fresh database
            flask db upgrade
            
            # Test migration rollback
            CURRENT_MIGRATION=$(flask db current)
            echo "Current migration: $CURRENT_MIGRATION"
            
            if [ -n "$CURRENT_MIGRATION" ] && [ "$CURRENT_MIGRATION" != "None" ]; then
              echo "Testing migration operations..."
              flask db upgrade head
              echo "✅ Migration validation passed"
            fi
            
            # Test with sample data
            python -c "
            from app import create_app, db
            from app.models.user import User
            from app.models.project import Project
            from app.models.client import Client
            
            app = create_app()
            with app.app_context():
                user = User(username='test_user', role='user')
                db.session.add(user)
                db.session.commit()
                
                client = Client(name='Test Client', description='Test client')
                db.session.add(client)
                db.session.commit()
                
                project = Project(name='Test Project', client_id=client.id, description='Test project')
                db.session.add(project)
                db.session.commit()
                print('✅ Sample data created and validated successfully')
            "
          else
            echo "ℹ️ No migration-related changes detected"
          fi

      - name: Initialize database schema for tests
        env:
          DATABASE_URL: postgresql://test_user:test_password@localhost:5432/test_db
          FLASK_APP: app.py
          FLASK_ENV: testing
        run: |
          echo "🔧 Applying migrations to ensure test schema exists..."
          flask db upgrade
          echo "Current migration: $(flask db current)"
      
      - name: Run complete test suite
        env:
          DATABASE_URL: postgresql://test_user:test_password@localhost:5432/test_db
          FLASK_APP: app.py
          FLASK_ENV: testing
          PYTHONPATH: ${{ github.workspace }}
        run: |
          pytest -v -n auto --cov=app --cov-report=xml --cov-report=html --cov-report=term-missing \
                 --junitxml=junit.xml --maxfail=5
      
      - name: Upload coverage reports
        uses: codecov/codecov-action@v4
        with:
          files: ./coverage.xml
          flags: release
          name: release-tests
      
      - name: Upload test results
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: test-results-release
          path: |
            htmlcov/
            coverage.xml
            junit.xml
      
      - name: Publish test results
        uses: EnricoMi/publish-unit-test-result-action@v2
        if: always()
        with:
          files: junit.xml
          check_name: Release Test Results

  # ============================================================================
  # Security Audit (always runs for releases)
  # ============================================================================
  security-audit:
    name: Security Audit
    runs-on: ubuntu-latest
    timeout-minutes: 10
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      
      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: ${{ env.PYTHON_VERSION }}
          cache: 'pip'
      
      - name: Install security tools
        run: |
          pip install 'safety==3.7.0'
      
      - name: Run Safety
        run: |
          mkdir -p .test_installation_config
          REPORT=".test_installation_config/safety-report.json"
          python -m safety check --file requirements.txt --save-json "${REPORT}" >/dev/null || true
          if [ -f "${REPORT}" ]; then
            python scripts/ci/sanitize_safety_report.py "${REPORT}" "${{ github.workspace }}"
          fi
          python -m safety check --file requirements.txt || true
      
      - name: Upload security reports
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: security-audit-reports
          path: |
            .test_installation_config/safety-report.json

  # ============================================================================
  # Determine Version
  # ============================================================================
  determine-version:
    name: Determine Version
    runs-on: ubuntu-latest
    outputs:
      version: ${{ steps.version.outputs.version }}
      is_prerelease: ${{ steps.version.outputs.is_prerelease }}
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      
      - name: Extract version from setup.py
        id: extract_version
        run: |
          # Extract version from setup.py
          VERSION=$(grep -oP "version='\K[^']+" setup.py)
          
          if [ -z "$VERSION" ]; then
            echo "❌ ERROR: Could not extract version from setup.py"
            exit 1
          fi
          
          echo "extracted_version=$VERSION" >> $GITHUB_OUTPUT
          echo "📝 Extracted version from setup.py: $VERSION"
      
      - name: Determine version and validate
        id: version
        run: |
          SETUP_VERSION="${{ steps.extract_version.outputs.extracted_version }}"
          
          if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
            # For manual triggers, use the input version
            VERSION="${{ github.event.inputs.version }}"
            IS_PRERELEASE="false"
          elif [[ "${{ github.event_name }}" == "release" ]]; then
            # For release events, use the release tag
            VERSION="${{ github.event.release.tag_name }}"
            IS_PRERELEASE="${{ github.event.release.prerelease }}"
          elif [[ "${GITHUB_REF}" == refs/tags/* ]]; then
            # For tag pushes, use the tag name
            VERSION=${GITHUB_REF#refs/tags/}
            IS_PRERELEASE="false"
          else
            # For push to main/master, use setup.py version
            VERSION="v${SETUP_VERSION}"
            IS_PRERELEASE="false"
          fi
          
          # Ensure version starts with 'v'
          if [[ ! $VERSION =~ ^v ]]; then
            VERSION="v${VERSION}"
          fi
          
          # Extract version without 'v' prefix for comparison
          VERSION_NO_V="${VERSION#v}"
          
          # Validate that the version matches setup.py
          if [[ "$VERSION_NO_V" != "$SETUP_VERSION" ]]; then
            echo "❌ ERROR: Version mismatch!"
            echo "   Tag version: $VERSION_NO_V"
            echo "   setup.py version: $SETUP_VERSION"
            echo ""
            echo "Please update setup.py to version '$VERSION_NO_V' or use version 'v$SETUP_VERSION'"
            exit 1
          fi
          
          # Check if tag already exists (prevent duplicates)
          if git rev-parse "$VERSION" >/dev/null 2>&1; then
            echo "❌ ERROR: Tag '$VERSION' already exists!"
            echo "   This version has already been released."
            echo "   Please update the version in setup.py to a new version number."
            exit 1
          fi
          
          echo "version=$VERSION" >> $GITHUB_OUTPUT
          echo "is_prerelease=$IS_PRERELEASE" >> $GITHUB_OUTPUT
          
          echo "✅ Version validation passed"
          echo "📦 Version: $VERSION"
          echo "🏷️ Prerelease: $IS_PRERELEASE"

  # ============================================================================
  # Build and Push Release Image
  # ============================================================================
  build-and-push:
    name: Build and Push Release Image
    runs-on: ubuntu-latest
    needs: [security-audit, determine-version]
    # Note: full-test-suite is optional, so we don't depend on it
    # Tests already ran on PR before merge
    permissions:
      contents: read
      packages: write
    timeout-minutes: 45
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      
      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3
      
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      
      - name: Log in to Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Log in to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN || secrets.DOCKERHUB_PASSWORD }}
        continue-on-error: true
      
      - name: Set lowercase image name
        id: image-name
        run: |
          # Convert repository name to lowercase for Docker image compatibility
          IMAGE_NAME_LOWER=$(echo "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}" | tr '[:upper:]' '[:lower:]')
          echo "IMAGE_NAME_LOWER=${IMAGE_NAME_LOWER}" >> $GITHUB_OUTPUT
          echo "✅ Image name set to: ${IMAGE_NAME_LOWER}"
      
      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          # Use lowercase image name for compatibility with Docker registries
          images: |
            ${{ steps.image-name.outputs.IMAGE_NAME_LOWER }}
            ${{ env.DOCKERHUB_IMAGE }}
          tags: |
            type=semver,pattern={{version}},value=${{ needs.determine-version.outputs.version }}
            type=semver,pattern={{major}}.{{minor}},value=${{ needs.determine-version.outputs.version }}
            type=semver,pattern={{major}},value=${{ needs.determine-version.outputs.version }}
            type=raw,value=latest,enable={{is_default_branch}}
            type=raw,value=stable,enable=${{ needs.determine-version.outputs.is_prerelease == 'false' }}
          labels: |
            org.opencontainers.image.description=Self-hosted time tracking web application for projects, clients, and reports.

      - name: Inject analytics configuration from GitHub Secrets
        env:
          OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.OTEL_EXPORTER_OTLP_ENDPOINT }}
          OTEL_EXPORTER_OTLP_TOKEN: ${{ secrets.OTEL_EXPORTER_OTLP_TOKEN }}
          SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
        run: |
          echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
          echo "🔐 INJECTING ANALYTICS CREDENTIALS FROM GITHUB SECRET STORE"
          echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
          echo ""
          echo "📍 Location: Settings → Secrets and variables → Actions"
          echo "📝 Target File: app/config/analytics_defaults.py"
          echo ""
          
          # Show file before injection
          echo "📄 File content BEFORE injection (showing placeholders):"
          echo "──────────────────────────────────────────────────────────────────"
          grep -E "(OTEL_EXPORTER_OTLP_ENDPOINT_DEFAULT|OTEL_EXPORTER_OTLP_TOKEN_DEFAULT|SENTRY_DSN_DEFAULT)" app/config/analytics_defaults.py || true
          echo "──────────────────────────────────────────────────────────────────"
          echo ""
          
          # Verify secrets are available
          echo "🔍 Verifying GitHub Secrets availability..."
          if [ -z "$OTEL_EXPORTER_OTLP_ENDPOINT" ] || [ -z "$OTEL_EXPORTER_OTLP_TOKEN" ]; then
            echo "⚠️  Grafana OTLP secrets not fully set (telemetry sink disabled in this build)"
            echo "   → To enable: Add OTEL_EXPORTER_OTLP_ENDPOINT and OTEL_EXPORTER_OTLP_TOKEN in Settings → Secrets"
          else
            echo "✅ Grafana OTLP secrets found in GitHub Secret Store"
          fi
          
          if [ -z "$SENTRY_DSN" ]; then
            echo "⚠️  SENTRY_DSN secret not set (optional)"
            echo "   → Sentry error tracking will be disabled"
          else
            echo "✅ SENTRY_DSN secret found in GitHub Secret Store"
            echo "   → Format: ${SENTRY_DSN:0:25}***${SENTRY_DSN: -10} (${#SENTRY_DSN} characters)"
          fi
          echo ""
          
          # Perform replacement (use empty string if secrets not set)
          echo "🔧 Injecting secrets into application configuration..."
          sed -i "s|%%OTEL_EXPORTER_OTLP_ENDPOINT_PLACEHOLDER%%|${OTEL_EXPORTER_OTLP_ENDPOINT:-}|g" app/config/analytics_defaults.py
          sed -i "s|%%OTEL_EXPORTER_OTLP_TOKEN_PLACEHOLDER%%|${OTEL_EXPORTER_OTLP_TOKEN:-}|g" app/config/analytics_defaults.py
          sed -i "s|%%SENTRY_DSN_PLACEHOLDER%%|${SENTRY_DSN:-}|g" app/config/analytics_defaults.py
          echo "   → Placeholders replaced with secret values (or empty if not set)"
          echo ""
          
          # Show file after injection (redacted)
          echo "📄 File content AFTER injection (secrets redacted):"
          echo "──────────────────────────────────────────────────────────────────"
          grep -E "(OTEL_EXPORTER_OTLP_ENDPOINT_DEFAULT|OTEL_EXPORTER_OTLP_TOKEN_DEFAULT|SENTRY_DSN_DEFAULT)" app/config/analytics_defaults.py | \
            sed 's/\(phc_[a-zA-Z0-9]\{8\}\)[a-zA-Z0-9]*\([a-zA-Z0-9]\{4\}\)/\1***\2/g' | \
            sed 's|\(https://[^@]*@[^/]*\)|***REDACTED***|g' || true
          echo "──────────────────────────────────────────────────────────────────"
          echo ""
          
          # Verify placeholders were replaced
          echo "🔍 Verifying injection was successful..."
          if grep -q "%%OTEL_EXPORTER_OTLP_ENDPOINT_PLACEHOLDER%%" app/config/analytics_defaults.py; then
            echo "❌ ERROR: Grafana endpoint placeholder was NOT replaced!"
            exit 1
          else
            echo "✅ Grafana endpoint placeholder successfully replaced"
          fi
          if grep -q "%%OTEL_EXPORTER_OTLP_TOKEN_PLACEHOLDER%%" app/config/analytics_defaults.py; then
            echo "❌ ERROR: Grafana token placeholder was NOT replaced!"
            exit 1
          else
            echo "✅ Grafana token placeholder successfully replaced"
          fi
          
          if grep -q "%%SENTRY_DSN_PLACEHOLDER%%" app/config/analytics_defaults.py; then
            echo "❌ ERROR: Sentry DSN placeholder was NOT replaced!"
            echo "   The placeholder '%%SENTRY_DSN_PLACEHOLDER%%' is still present in the file."
            exit 1
          else
            echo "✅ Sentry DSN placeholder successfully replaced"
          fi
          
          echo "✅ Grafana OTLP values injected"
          echo ""
          
          echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
          echo "✅ SUCCESS: Analytics credentials injected from GitHub Secret Store"
          echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
          echo ""
          echo "📊 Injected Credentials Summary:"
          if [ -n "$OTEL_EXPORTER_OTLP_ENDPOINT" ] && [ -n "$OTEL_EXPORTER_OTLP_TOKEN" ]; then
            echo "   • Grafana OTLP: configured ✓"
          else
            echo "   • Grafana OTLP: [Not configured - telemetry sink disabled] ⚠️"
          fi
          if [ -n "$SENTRY_DSN" ]; then
            echo "   • Sentry DSN: ${SENTRY_DSN:0:20}*** ✓"
          else
            echo "   • Sentry DSN: [Not configured] ⚠️"
          fi
          echo ""
          echo "🔒 Security Notes:"
          echo "   • Secrets are injected at build time from GitHub Secret Store"
          echo "   • Secrets are never exposed in logs or build artifacts"
          echo "   • Users can still opt-in/opt-out of telemetry via admin dashboard"
          echo "" 
      
      - name: Inject donate-hide public key (optional)
        env:
          DONATE_HIDE_PUBLIC_KEY_PEM: ${{ secrets.DONATE_HIDE_PUBLIC_KEY_PEM }}
        run: |
          if [ -n "$DONATE_HIDE_PUBLIC_KEY_PEM" ]; then
            echo "✅ DONATE_HIDE_PUBLIC_KEY_PEM secret set — writing donate_hide_public.pem for Docker build"
            echo "$DONATE_HIDE_PUBLIC_KEY_PEM" > donate_hide_public.pem
            echo "   → File will be copied into image at /app/donate_hide_public.pem (DONATE_HIDE_PUBLIC_KEY_FILE)"
          else
            echo "⚠️  DONATE_HIDE_PUBLIC_KEY_PEM not set — Support visibility code verification will be disabled in image"
          fi
      
      - name: Build and push Docker image
        uses: docker/build-push-action@v5
        with:
          context: .
          platforms: linux/amd64,linux/arm64
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          build-args: |
            APP_VERSION=${{ needs.determine-version.outputs.version }}
          cache-from: type=registry,ref=${{ steps.image-name.outputs.IMAGE_NAME_LOWER }}:latest
          cache-to: type=inline
      
      - name: Generate deployment manifests
        run: |
          VERSION="${{ needs.determine-version.outputs.version }}"
          # Remove 'v' prefix for image tag
          VERSION_NO_V="${VERSION#v}"
          # Convert repository name to lowercase for image name
          IMAGE_NAME_LOWER=$(echo "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}" | tr '[:upper:]' '[:lower:]')
          PRODUCTION_IMAGE="${IMAGE_NAME_LOWER}:${VERSION_NO_V}"
          
          # Docker Compose deployment - includes all services from docker-compose.yml
          cat > docker-compose.production.yml << EOF
          # TimeTracker Production Deployment
          # Generated: $(date -u +'%Y-%m-%d %H:%M:%S UTC')
          # Version: ${VERSION}
          # Image: ${PRODUCTION_IMAGE}
          
          services:
            # Certificate generator - runs once to create self-signed certs with SANs
            certgen:
              image: alpine:latest
              container_name: timetracker-certgen
              volumes:
                - ./nginx/ssl:/certs
                - ./scripts:/scripts:ro
              command: sh /scripts/generate-certs.sh
              restart: "no"
          
            # HTTPS reverse proxy (TLS terminates here)
            nginx:
              image: nginx:alpine
              container_name: timetracker-nginx
              ports:
                - "80:80"
                - "443:443"
              volumes:
                - ./nginx/conf.d:/etc/nginx/conf.d:ro
                - ./nginx/ssl:/etc/nginx/ssl:ro
              depends_on:
                certgen:
                  condition: service_completed_successfully
                app:
                  condition: service_started
              restart: unless-stopped
          
            app:
              image: ${PRODUCTION_IMAGE}
              container_name: timetracker-app
              environment:
                - TZ=\${TZ:-Europe/Brussels}
                - CURRENCY=\${CURRENCY:-EUR}
                - ROUNDING_MINUTES=\${ROUNDING_MINUTES:-1}
                - SINGLE_ACTIVE_TIMER=\${SINGLE_ACTIVE_TIMER:-true}
                - ALLOW_SELF_REGISTER=\${ALLOW_SELF_REGISTER:-true}
                - IDLE_TIMEOUT_MINUTES=\${IDLE_TIMEOUT_MINUTES:-30}
                - ADMIN_USERNAMES=\${ADMIN_USERNAMES:-admin}
                # IMPORTANT: Change SECRET_KEY in production! Used for sessions and CSRF tokens.
                # Generate a secure key: python -c "import secrets; print(secrets.token_hex(32))"
                # 
                # CSRF CONFIGURATION:
                # - WTF_CSRF_SSL_STRICT: Set to 'false' for HTTP access (localhost or IP address)
                #   Set to 'true' only when using HTTPS in production
                # - If accessing via IP address (e.g., 192.168.1.100), also set:
                #   SESSION_COOKIE_SECURE=false and CSRF_COOKIE_SECURE=false
                # 
                # TROUBLESHOOTING: If forms fail with "CSRF token missing or invalid":
                # 1. Verify SECRET_KEY is set and doesn't change between restarts
                # 2. Check CSRF is enabled: WTF_CSRF_ENABLED=true
                # 3. Ensure cookies are enabled in your browser
                # 4. If behind a reverse proxy, ensure it forwards cookies correctly
                # 5. Check the token hasn't expired (increase WTF_CSRF_TIME_LIMIT if needed)
                # 6. If accessing via IP (not localhost): WTF_CSRF_SSL_STRICT=false
                # For details: docs/CSRF_CONFIGURATION.md and docs/CSRF_IP_ACCESS_GUIDE.md
                - SECRET_KEY=\${SECRET_KEY:-your-secret-key-change-this}
                # Disable strict Referer check by default to avoid privacy/port issues
                - WTF_CSRF_SSL_STRICT=\${WTF_CSRF_SSL_STRICT:-true}
                - WTF_CSRF_ENABLED=\${WTF_CSRF_ENABLED:-true}
                - WTF_CSRF_TIME_LIMIT=\${WTF_CSRF_TIME_LIMIT:-3600}
                - SESSION_COOKIE_SECURE=\${SESSION_COOKIE_SECURE:-true}
                - SESSION_COOKIE_SAMESITE=\${SESSION_COOKIE_SAMESITE:-Lax}
                - REMEMBER_COOKIE_SECURE=\${REMEMBER_COOKIE_SECURE:-true}
                - CSRF_COOKIE_SECURE=\${CSRF_COOKIE_SECURE:-true}
                - CSRF_COOKIE_HTTPONLY=\${CSRF_COOKIE_HTTPONLY:-false}
                - CSRF_COOKIE_SAMESITE=\${CSRF_COOKIE_SAMESITE:-Lax}
                - CSRF_COOKIE_NAME=\${CSRF_COOKIE_NAME:-XSRF-TOKEN}
                - PREFERRED_URL_SCHEME=\${PREFERRED_URL_SCHEME:-https}
                - WTF_CSRF_TRUSTED_ORIGINS=\${WTF_CSRF_TRUSTED_ORIGINS:-https://localhost}
                - DATABASE_URL=postgresql+psycopg2://timetracker:timetracker@db:5432/timetracker
                - REDIS_URL=redis://:\${REDIS_PASSWORD:-timetracker}@redis:6379/0
                - REDIS_ENABLED=\${REDIS_ENABLED:-true}
                - LOG_FILE=/app/logs/timetracker.log
                # Analytics & Monitoring (optional)
                # See docs/analytics.md for configuration details
                - SENTRY_DSN=\${SENTRY_DSN:-}
                - SENTRY_TRACES_RATE=\${SENTRY_TRACES_RATE:-0.0}
                - OTEL_EXPORTER_OTLP_ENDPOINT=\${OTEL_EXPORTER_OTLP_ENDPOINT:-}
                - OTEL_EXPORTER_OTLP_TOKEN=\${OTEL_EXPORTER_OTLP_TOKEN:-}
                - ENABLE_TELEMETRY=\${ENABLE_TELEMETRY:-false}
                - TELE_SALT=\${TELE_SALT:-8f4a7b2e9c1d6f3a5e8b4c7d2a9f6e3b1c8d5a7f2e9b4c6d3a8f5e1b7c4d9a2f}
          
              # Expose only internally; nginx publishes ports
              ports: []
              volumes:
                - app_data:/data
                - app_logs:/app/logs
                - app_uploads:/app/app/static/uploads
              depends_on:
                db:
                  condition: service_healthy
                redis:
                  condition: service_healthy
              restart: unless-stopped
              healthcheck:
                test: ["CMD", "curl", "-f", "http://localhost:8080/_health"]
                interval: 30s
                timeout: 10s
                retries: 3
                start_period: 40s
          
            db:
              image: postgres:16-alpine
              container_name: timetracker-db
              environment:
                - POSTGRES_DB=\${POSTGRES_DB:-timetracker}
                - POSTGRES_USER=\${POSTGRES_USER:-timetracker}
                - POSTGRES_PASSWORD=\${POSTGRES_PASSWORD:-timetracker}
                - TZ=\${TZ:-Europe/Brussels}
              volumes:
                - db_data:/var/lib/postgresql/data
              healthcheck:
                test: ["CMD-SHELL", "pg_isready -U \$\$POSTGRES_USER -d \$\$POSTGRES_DB"]
                interval: 10s
                timeout: 5s
                retries: 5
                start_period: 30s
              restart: unless-stopped
          
            # Redis - Caching and session storage
            redis:
              image: redis:7-alpine
              container_name: timetracker-redis
              command: redis-server --appendonly yes --requirepass \${REDIS_PASSWORD:-timetracker}
              volumes:
                - redis_data:/data
              ports:
                - "6379:6379"
              healthcheck:
                test: ["CMD", "redis-cli", "--raw", "incr", "ping"]
                interval: 10s
                timeout: 3s
                retries: 5
              restart: unless-stopped
          
            # Analytics & Monitoring Services
            # All services start by default for complete monitoring
            # See docs/analytics.md and ANALYTICS_QUICK_START.md for details
          
            # Prometheus - Metrics collection and storage
            prometheus:
              image: prom/prometheus:latest
              container_name: timetracker-prometheus
              volumes:
                - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
                - prometheus_data:/prometheus
              command:
                - '--config.file=/etc/prometheus/prometheus.yml'
                - '--storage.tsdb.path=/prometheus'
                - '--storage.tsdb.retention.time=30d'
              ports:
                - "9090:9090"
              restart: unless-stopped
          
            # Grafana - Metrics visualization and dashboards
            grafana:
              image: grafana/grafana:latest
              container_name: timetracker-grafana
              environment:
                - GF_SECURITY_ADMIN_PASSWORD=\${GRAFANA_ADMIN_PASSWORD:-admin}
                - GF_USERS_ALLOW_SIGN_UP=false
                - GF_SERVER_ROOT_URL=\${GF_SERVER_ROOT_URL:-http://localhost:3000}
              volumes:
                - grafana_data:/var/lib/grafana
                - ./grafana/provisioning:/etc/grafana/provisioning
              ports:
                - "3000:3000"
              depends_on:
                - prometheus
              restart: unless-stopped
          
            # Loki - Log aggregation
            loki:
              image: grafana/loki:latest
              container_name: timetracker-loki
              volumes:
                - ./loki/loki-config.yml:/etc/loki/local-config.yaml
                - loki_data:/loki
              ports:
                - "3100:3100"
              command: -config.file=/etc/loki/local-config.yaml
              restart: unless-stopped
          
            # Promtail - Log shipping to Loki
            promtail:
              image: grafana/promtail:latest
              container_name: timetracker-promtail
              volumes:
                - ./logs:/var/log/timetracker:ro
                - ./promtail/promtail-config.yml:/etc/promtail/config.yml
              command: -config.file=/etc/promtail/config.yml
              depends_on:
                - loki
              restart: unless-stopped
          
          volumes:
            app_data:
              driver: local
            app_logs:
              driver: local
            app_uploads:
              driver: local
            db_data:
              driver: local
            prometheus_data:
              driver: local
            grafana_data:
              driver: local
            loki_data:
              driver: local
            redis_data:
              driver: local
          EOF
          
          # Kubernetes deployment (basic example)
          cat > k8s-deployment.yml << EOF
          # Kubernetes Deployment for TimeTracker ${VERSION}
          apiVersion: apps/v1
          kind: Deployment
          metadata:
            name: timetracker
            labels:
              app: timetracker
              version: ${VERSION}
          spec:
            replicas: 2
            selector:
              matchLabels:
                app: timetracker
            template:
              metadata:
                labels:
                  app: timetracker
                  version: ${VERSION}
              spec:
                containers:
                - name: timetracker
                  image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${VERSION}
                  ports:
                  - containerPort: 8080
                    name: http
                  env:
                  - name: DATABASE_URL
                    valueFrom:
                      secretKeyRef:
                        name: timetracker-secrets
                        key: database-url
                  - name: SECRET_KEY
                    valueFrom:
                      secretKeyRef:
                        name: timetracker-secrets
                        key: secret-key
                  livenessProbe:
                    httpGet:
                      path: /_health
                      port: 8080
                    initialDelaySeconds: 30
                    periodSeconds: 10
                  readinessProbe:
                    httpGet:
                      path: /_health
                      port: 8080
                    initialDelaySeconds: 5
                    periodSeconds: 5
          ---
          apiVersion: v1
          kind: Service
          metadata:
            name: timetracker
          spec:
            selector:
              app: timetracker
            ports:
            - protocol: TCP
              port: 80
              targetPort: 8080
            type: LoadBalancer
          EOF
          
          echo "📄 Deployment manifests created"
      
      - name: Upload deployment manifests
        uses: actions/upload-artifact@v4
        with:
          name: deployment-manifests-${{ needs.determine-version.outputs.version }}
          path: |
            docker-compose.production.yml
            k8s-deployment.yml

  # ============================================================================
  # Build Desktop Applications
  # ============================================================================
  build-desktop-windows:
    name: Build Desktop - Windows
    runs-on: windows-latest
    needs: [determine-version]
    continue-on-error: true
    timeout-minutes: 30
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
      
      - name: Generate desktop icons
        run: |
          npm install
          npm run generate:icons
      
      - name: Install dependencies
        working-directory: desktop
        run: npm ci
      
      - name: Build Windows
        working-directory: desktop
        run: npm run build:win
      
      - name: Upload Windows installer
        if: success()
        uses: actions/upload-artifact@v4
        with:
          name: desktop-windows-${{ needs.determine-version.outputs.version }}
          path: desktop/dist/*.exe
          retention-days: 90

  build-desktop-linux:
    name: Build Desktop - Linux
    runs-on: ubuntu-latest
    needs: [determine-version]
    continue-on-error: true
    timeout-minutes: 30
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
      
      - name: Generate desktop icons
        run: |
          npm install
          npm run generate:icons
      
      - name: Install dependencies
        working-directory: desktop
        run: npm ci
      
      - name: Build Linux
        working-directory: desktop
        run: npm run build:linux
      
      - name: Upload Linux packages
        if: success()
        uses: actions/upload-artifact@v4
        with:
          name: desktop-linux-${{ needs.determine-version.outputs.version }}
          path: |
            desktop/dist/*.AppImage
            desktop/dist/*.deb
          retention-days: 90

  build-desktop-macos:
    name: Build Desktop - macOS
    runs-on: macos-latest
    needs: [determine-version]
    continue-on-error: true
    timeout-minutes: 30
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
      
      - name: Generate desktop icons
        run: |
          npm install
          npm run generate:icons
          chmod +x scripts/generate-macos-icon.sh
          ./scripts/generate-macos-icon.sh
      
      - name: Install dependencies
        working-directory: desktop
        run: npm ci
      
      - name: Build macOS
        working-directory: desktop
        run: npm run build:mac
      
      - name: Upload macOS DMG
        if: success()
        uses: actions/upload-artifact@v4
        with:
          name: desktop-macos-${{ needs.determine-version.outputs.version }}
          path: desktop/dist/*.dmg
          retention-days: 90

  # ============================================================================
  # Build Mobile Applications
  # ============================================================================
  build-mobile-android:
    name: Build Mobile - Android
    runs-on: ubuntu-latest
    needs: [determine-version]
    continue-on-error: true
    timeout-minutes: 45
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      
      - name: Setup Java
        uses: actions/setup-java@v3
        with:
          distribution: 'zulu'
          java-version: '17'
      
      - name: Setup Flutter
        uses: subosito/flutter-action@v2
        with:
          flutter-version: '3.35.4'
          channel: 'stable'
      
      - name: Generate app icons
        run: |
          pip install Pillow
          python scripts/generate-mobile-icon.py
      
      - name: Install dependencies
        working-directory: mobile
        run: flutter pub get
      
      - name: Generate launcher icons
        working-directory: mobile
        run: dart run flutter_launcher_icons
      
      - name: Run mobile tests
        working-directory: mobile
        run: flutter test
      
      - name: Build APK
        working-directory: mobile
        env:
          OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.OTEL_EXPORTER_OTLP_ENDPOINT }}
          OTEL_EXPORTER_OTLP_TOKEN: ${{ secrets.OTEL_EXPORTER_OTLP_TOKEN }}
        run: |
          flutter build apk --release \
            --dart-define=OTEL_EXPORTER_OTLP_ENDPOINT="${OTEL_EXPORTER_OTLP_ENDPOINT:-}" \
            --dart-define=OTEL_EXPORTER_OTLP_TOKEN="${OTEL_EXPORTER_OTLP_TOKEN:-}"
      
      - name: Build App Bundle
        working-directory: mobile
        env:
          OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.OTEL_EXPORTER_OTLP_ENDPOINT }}
          OTEL_EXPORTER_OTLP_TOKEN: ${{ secrets.OTEL_EXPORTER_OTLP_TOKEN }}
        run: |
          flutter build appbundle --release \
            --dart-define=OTEL_EXPORTER_OTLP_ENDPOINT="${OTEL_EXPORTER_OTLP_ENDPOINT:-}" \
            --dart-define=OTEL_EXPORTER_OTLP_TOKEN="${OTEL_EXPORTER_OTLP_TOKEN:-}"
        continue-on-error: true
      
      - name: Upload Android APK
        if: success()
        uses: actions/upload-artifact@v4
        with:
          name: mobile-android-apk-${{ needs.determine-version.outputs.version }}
          path: mobile/build/app/outputs/flutter-apk/app-release.apk
          retention-days: 90
      
      - name: Upload Android App Bundle
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: mobile-android-aab-${{ needs.determine-version.outputs.version }}
          path: mobile/build/app/outputs/bundle/release/app-release.aab
          if-no-files-found: ignore
          retention-days: 90

  build-mobile-ios:
    name: Build Mobile - iOS
    runs-on: macos-latest
    needs: [determine-version]
    continue-on-error: true
    timeout-minutes: 45
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      
      - name: Setup Flutter
        uses: subosito/flutter-action@v2
        with:
          flutter-version: '3.35.4'
          channel: 'stable'
      
      - name: Generate app icons
        run: |
          pip install Pillow
          python scripts/generate-mobile-icon.py
      
      - name: Install dependencies
        working-directory: mobile
        run: flutter pub get
      
      - name: Generate iOS platform files
        working-directory: mobile
        run: flutter create --platforms=ios .
      
      - name: Generate launcher icons
        working-directory: mobile
        run: |
          dart run flutter_launcher_icons
          dart run flutter_launcher_icons -f flutter_launcher_icons_ios.yaml
      
      - name: Run mobile tests
        working-directory: mobile
        run: flutter test
      
      # Release mode is invalid for iOS simulator; this validates compilation and produces Runner.app for the artifact zip.
      - name: Build iOS (simulator)
        working-directory: mobile
        env:
          OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.OTEL_EXPORTER_OTLP_ENDPOINT }}
          OTEL_EXPORTER_OTLP_TOKEN: ${{ secrets.OTEL_EXPORTER_OTLP_TOKEN }}
        run: |
          flutter build ios --simulator \
            --dart-define=OTEL_EXPORTER_OTLP_ENDPOINT="${OTEL_EXPORTER_OTLP_ENDPOINT:-}" \
            --dart-define=OTEL_EXPORTER_OTLP_TOKEN="${OTEL_EXPORTER_OTLP_TOKEN:-}"
      
      - name: Create iOS archive
        if: success()
        working-directory: mobile
        run: |
          mkdir -p dist
          if [ -d "build/ios/iphonesimulator/Runner.app" ]; then
            cd build/ios/iphonesimulator
            zip -r ../../../dist/TimeTracker-iOS-${{ needs.determine-version.outputs.version }}.zip Runner.app
            cd ../../..
            echo "✅ iOS simulator archive created successfully"
            ls -lh dist/
          else
            echo "❌ ERROR: Runner.app not found at build/ios/iphonesimulator/Runner.app"
            ls -la build/ios/ || true
            ls -la build/ || true
            find build -name "Runner.app" -type d 2>/dev/null || true
            exit 1
          fi
      
      - name: Upload iOS build
        if: success()
        uses: actions/upload-artifact@v4
        with:
          name: mobile-ios-${{ needs.determine-version.outputs.version }}
          path: mobile/dist/*.zip
          if-no-files-found: error
          retention-days: 90

  # ============================================================================
  # Create GitHub Release
  # ============================================================================
  create-release:
    name: Create GitHub Release
    runs-on: ubuntu-latest
    needs: [build-and-push, determine-version, build-desktop-windows, build-desktop-linux, build-desktop-macos, build-mobile-android, build-mobile-ios]
    if: github.event_name != 'release'
    permissions:
      contents: write
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      
      - name: Download deployment manifests
        uses: actions/download-artifact@v4
        continue-on-error: true
        with:
          name: deployment-manifests-${{ needs.determine-version.outputs.version }}
      
      - name: Download desktop artifacts
        uses: actions/download-artifact@v4
        continue-on-error: true
        with:
          pattern: 'desktop-*'
          merge-multiple: false
      
      - name: Download mobile artifacts
        uses: actions/download-artifact@v4
        continue-on-error: true
        with:
          pattern: 'mobile-*'
          merge-multiple: false
      
      - name: Prepare release files
        run: |
          VERSION="${{ needs.determine-version.outputs.version }}"
          RELEASE_DIR="release-files"
          mkdir -p "$RELEASE_DIR"
          
          # Move deployment manifests to release directory
          if [ -f "docker-compose.production.yml" ]; then
            cp docker-compose.production.yml "$RELEASE_DIR/" || true
          fi
          if [ -f "k8s-deployment.yml" ]; then
            cp k8s-deployment.yml "$RELEASE_DIR/" || true
          fi
          
          # Organize desktop files
          DESKTOP_DIR="$RELEASE_DIR/desktop"
          mkdir -p "$DESKTOP_DIR"
          
          # Windows
          if [ -d "desktop-windows-$VERSION" ]; then
            mkdir -p "$DESKTOP_DIR/windows"
            cp desktop-windows-$VERSION/* "$DESKTOP_DIR/windows/" 2>/dev/null || true
          fi
          
          # Linux
          if [ -d "desktop-linux-$VERSION" ]; then
            mkdir -p "$DESKTOP_DIR/linux"
            cp desktop-linux-$VERSION/* "$DESKTOP_DIR/linux/" 2>/dev/null || true
          fi
          
          # macOS
          if [ -d "desktop-macos-$VERSION" ]; then
            mkdir -p "$DESKTOP_DIR/macos"
            cp desktop-macos-$VERSION/* "$DESKTOP_DIR/macos/" 2>/dev/null || true
          fi
          
          # Organize mobile files
          MOBILE_DIR="$RELEASE_DIR/mobile"
          mkdir -p "$MOBILE_DIR"
          
          # Android APK
          if [ -d "mobile-android-apk-$VERSION" ]; then
            mkdir -p "$MOBILE_DIR/android"
            cp mobile-android-apk-$VERSION/* "$MOBILE_DIR/android/" 2>/dev/null || true
          fi
          
          # Android AAB
          if [ -d "mobile-android-aab-$VERSION" ]; then
            mkdir -p "$MOBILE_DIR/android"
            cp mobile-android-aab-$VERSION/* "$MOBILE_DIR/android/" 2>/dev/null || true
          fi
          
          # iOS
          if [ -d "mobile-ios-$VERSION" ]; then
            mkdir -p "$MOBILE_DIR/ios"
            cp mobile-ios-$VERSION/* "$MOBILE_DIR/ios/" 2>/dev/null || true
          fi
          
          # Create file list for release (for debugging)
          find "$RELEASE_DIR" -type f > release-files-list.txt || true
          echo "Files to attach to release:"
          cat release-files-list.txt || echo "No files found"
          
          # Count files for summary
          FILE_COUNT=$(find "$RELEASE_DIR" -type f | wc -l || echo "0")
          echo "Total files to attach: $FILE_COUNT"
          echo "file_count=$FILE_COUNT" >> $GITHUB_OUTPUT
      
      - name: Generate changelog
        id: changelog
        run: |
          VERSION="${{ needs.determine-version.outputs.version }}"
          
          # Try to get previous tag
          PREVIOUS_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
          
          if [ -n "$PREVIOUS_TAG" ]; then
            CHANGELOG=$(git log ${PREVIOUS_TAG}..HEAD --pretty=format:"- %s (%an)" --no-merges)
          else
            CHANGELOG="Initial release"
          fi
          
          # Write changelog to file first
          echo "$CHANGELOG" > CHANGELOG.md
          
          # Add build status to changelog
          echo "" >> CHANGELOG.md
          echo "" >> CHANGELOG.md
          echo "## 📦 Build Status" >> CHANGELOG.md
          echo "" >> CHANGELOG.md
          
          # Desktop builds
          echo "### Desktop Applications" >> CHANGELOG.md
          if [ "${{ needs.build-desktop-windows.result }}" == "success" ]; then
            echo "✅ Windows build: Success" >> CHANGELOG.md
          else
            echo "⚠️ Windows build: Failed or skipped" >> CHANGELOG.md
          fi
          if [ "${{ needs.build-desktop-linux.result }}" == "success" ]; then
            echo "✅ Linux build: Success" >> CHANGELOG.md
          else
            echo "⚠️ Linux build: Failed or skipped" >> CHANGELOG.md
          fi
          if [ "${{ needs.build-desktop-macos.result }}" == "success" ]; then
            echo "✅ macOS build: Success" >> CHANGELOG.md
          else
            echo "⚠️ macOS build: Failed or skipped" >> CHANGELOG.md
          fi
          
          # Mobile builds
          echo "" >> CHANGELOG.md
          echo "### Mobile Applications" >> CHANGELOG.md
          if [ "${{ needs.build-mobile-android.result }}" == "success" ]; then
            echo "✅ Android build: Success" >> CHANGELOG.md
          else
            echo "⚠️ Android build: Failed or skipped" >> CHANGELOG.md
          fi
          if [ "${{ needs.build-mobile-ios.result }}" == "success" ]; then
            echo "✅ iOS build: Success" >> CHANGELOG.md
          else
            echo "⚠️ iOS build: Failed or skipped" >> CHANGELOG.md
          fi
      
      - name: Create Release
        uses: softprops/action-gh-release@v1
        with:
          tag_name: ${{ needs.determine-version.outputs.version }}
          name: Release ${{ needs.determine-version.outputs.version }}
          body_path: CHANGELOG.md
          draft: false
          prerelease: ${{ needs.determine-version.outputs.is_prerelease }}
          files: |
            release-files/docker-compose.production.yml
            release-files/k8s-deployment.yml
            release-files/desktop/**/*
            release-files/mobile/**/*
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        continue-on-error: false

  # ============================================================================
  # Trigger Demo Site Deploy (Render)
  # ============================================================================
  trigger-demo-deploy:
    name: Trigger Demo Deploy
    runs-on: ubuntu-latest
    needs: [build-and-push]
    continue-on-error: true
    timeout-minutes: 2
    
    steps:
      - name: Trigger Render deploy hook
        env:
          RENDER_DEPLOY_HOOK_URL: ${{ secrets.TimeTrackerDemoRender }}
        run: |
          if [ -z "$RENDER_DEPLOY_HOOK_URL" ]; then
            echo "⚠️ TimeTrackerDemoRender secret not configured - skipping demo deploy"
            exit 0
          fi
          echo "🚀 Triggering Render deploy hook for demo site..."
          HTTP_CODE=$(curl -s -o /tmp/render-response.txt -w "%{http_code}" -X POST "$RENDER_DEPLOY_HOOK_URL")
          if [ "$HTTP_CODE" -ge 200 ] && [ "$HTTP_CODE" -lt 300 ]; then
            echo "✅ Render deploy triggered successfully (HTTP $HTTP_CODE)"
          else
            echo "❌ Render deploy hook returned HTTP $HTTP_CODE"
            cat /tmp/render-response.txt || true
            exit 1
          fi

  # ============================================================================
  # Post-Release Summary
  # ============================================================================
  release-summary:
    name: Release Summary
    runs-on: ubuntu-latest
    needs: [security-audit, build-and-push, determine-version, create-release, trigger-demo-deploy, build-desktop-windows, build-desktop-linux, build-desktop-macos, build-mobile-android, build-mobile-ios]
    if: always()
    
    steps:
      - name: Create release summary
        run: |
          echo "## 🚀 Release ${{ needs.determine-version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY
          echo "### Core Build Status" >> $GITHUB_STEP_SUMMARY
          echo "- ✅ Security: ${{ needs.security-audit.result }}" >> $GITHUB_STEP_SUMMARY
          echo "- ✅ Docker Build: ${{ needs.build-and-push.result }}" >> $GITHUB_STEP_SUMMARY
          echo "- ✅ Release: ${{ needs.create-release.result }}" >> $GITHUB_STEP_SUMMARY
          echo "- ✅ Demo Deploy: ${{ needs.trigger-demo-deploy.result }}" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY
          echo "### Desktop Applications" >> $GITHUB_STEP_SUMMARY
          if [ "${{ needs.build-desktop-windows.result }}" == "success" ]; then
            echo "- ✅ Windows: Success" >> $GITHUB_STEP_SUMMARY
          else
            echo "- ⚠️ Windows: ${{ needs.build-desktop-windows.result }}" >> $GITHUB_STEP_SUMMARY
          fi
          if [ "${{ needs.build-desktop-linux.result }}" == "success" ]; then
            echo "- ✅ Linux: Success" >> $GITHUB_STEP_SUMMARY
          else
            echo "- ⚠️ Linux: ${{ needs.build-desktop-linux.result }}" >> $GITHUB_STEP_SUMMARY
          fi
          if [ "${{ needs.build-desktop-macos.result }}" == "success" ]; then
            echo "- ✅ macOS: Success" >> $GITHUB_STEP_SUMMARY
          else
            echo "- ⚠️ macOS: ${{ needs.build-desktop-macos.result }}" >> $GITHUB_STEP_SUMMARY
          fi
          echo "" >> $GITHUB_STEP_SUMMARY
          echo "### Mobile Applications" >> $GITHUB_STEP_SUMMARY
          if [ "${{ needs.build-mobile-android.result }}" == "success" ]; then
            echo "- ✅ Android: Success" >> $GITHUB_STEP_SUMMARY
          else
            echo "- ⚠️ Android: ${{ needs.build-mobile-android.result }}" >> $GITHUB_STEP_SUMMARY
          fi
          if [ "${{ needs.build-mobile-ios.result }}" == "success" ]; then
            echo "- ✅ iOS: Success" >> $GITHUB_STEP_SUMMARY
          else
            echo "- ⚠️ iOS: ${{ needs.build-mobile-ios.result }}" >> $GITHUB_STEP_SUMMARY
          fi
          echo "" >> $GITHUB_STEP_SUMMARY
          echo "ℹ️ *Full test suite already ran on PR before merge*" >> $GITHUB_STEP_SUMMARY
          echo "ℹ️ *Desktop and mobile builds are optional - release continues even if they fail*" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY
          echo "### 🔐 Analytics Configuration" >> $GITHUB_STEP_SUMMARY
          echo "Analytics credentials were **successfully injected** from GitHub Secret Store:" >> $GITHUB_STEP_SUMMARY
          echo "- ✅ **OTLP**: Injected from \`OTEL_EXPORTER_OTLP_ENDPOINT\` + \`OTEL_EXPORTER_OTLP_TOKEN\` secrets" >> $GITHUB_STEP_SUMMARY
          echo "- ✅ **Sentry DSN**: Injected from \`SENTRY_DSN\` secret" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY
          echo "> 📍 **Secret Location**: Repository Settings → Secrets and variables → Actions" >> $GITHUB_STEP_SUMMARY
          echo "> 🔒 **Security**: Secrets are embedded at build time and never exposed in logs" >> $GITHUB_STEP_SUMMARY
          echo "> 👥 **Privacy**: Users maintain full control via opt-in/opt-out in admin dashboard" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY
          
          VERSION="${{ needs.determine-version.outputs.version }}"
          VERSION_NO_V="${VERSION#v}"
          
          echo "### 🐳 Docker Images" >> $GITHUB_STEP_SUMMARY
          echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
          echo "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${VERSION_NO_V}" >> $GITHUB_STEP_SUMMARY
          echo "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest" >> $GITHUB_STEP_SUMMARY
          echo "${{ env.DOCKERHUB_IMAGE }}:${VERSION_NO_V}" >> $GITHUB_STEP_SUMMARY
          echo "${{ env.DOCKERHUB_IMAGE }}:latest" >> $GITHUB_STEP_SUMMARY
          echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY
          echo "### 📦 Quick Deploy" >> $GITHUB_STEP_SUMMARY
          echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY
          echo "# Pull the image" >> $GITHUB_STEP_SUMMARY
          echo "docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${VERSION_NO_V}" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY
          echo "# Deploy with docker-compose" >> $GITHUB_STEP_SUMMARY
          echo "docker-compose -f docker-compose.production.yml up -d" >> $GITHUB_STEP_SUMMARY
          echo "\`\`\`" >> $GITHUB_STEP_SUMMARY

