name: Docker E2E Tests # 在 Docker 環境中運行完整 E2E 測試 # 包含所有轉換工具和翻譯服務 on: push: branches: ["main"] paths: - "src/**" - "tests/**" - "Dockerfile" - ".github/workflows/docker-e2e-tests.yml" pull_request: branches: ["main"] paths: - "src/**" - "tests/**" - "Dockerfile" workflow_dispatch: inputs: run_translation_tests: description: "Run translation tests (requires API keys)" required: false default: false type: boolean env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }} concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: # ============================================ # Job 1: Build test image # ============================================ build-test-image: name: Build Test Image runs-on: ubuntu-24.04 permissions: contents: read packages: write outputs: image: ${{ steps.image.outputs.image }} steps: - name: Checkout repository uses: actions/checkout@v4 # 使用專門的 action 進行激進的磁碟清理 - name: Free disk space (aggressive) uses: jlumbroso/free-disk-space@main with: tool-cache: true android: true dotnet: true haskell: true large-packages: true docker-images: true swap-storage: true - name: Additional disk cleanup run: | echo "🧹 Additional disk cleanup..." sudo rm -rf /usr/local/share/boost || true sudo rm -rf /usr/share/swift || true sudo rm -rf /opt/hostedtoolcache/CodeQL || true docker system prune -af --volumes || true echo "📊 Available space after cleanup:" df -h / - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Login to GitHub Container Registry uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: downcase REPO run: echo "REPO=${GITHUB_REPOSITORY@L}" >> "${GITHUB_ENV}" - name: Build and push test image uses: docker/build-push-action@v6 with: context: . push: true tags: ${{ env.REGISTRY }}/${{ env.REPO }}:test-${{ github.sha }} cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.REPO }}:buildcache-linux-amd64 cache-to: type=inline # 🔥 CACHE_BUST 強制重新執行模型下載層 build-args: | CACHE_BUST=${{ github.run_id }}${{ github.run_attempt }} - name: Set image output id: image run: echo "image=${{ env.REGISTRY }}/${{ env.REPO }}:test-${{ github.sha }}" >> $GITHUB_OUTPUT # ============================================ # Job 2: Run E2E tests in Docker # ============================================ e2e-tests: name: E2E Tests in Docker runs-on: ubuntu-24.04 needs: build-test-image timeout-minutes: 30 services: # Run the test image as a service convertx: image: ${{ needs.build-test-image.outputs.image }} credentials: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} ports: - 3000:3000 options: >- --health-cmd "curl -f http://localhost:3000/healthcheck || exit 1" --health-interval 10s --health-timeout 5s --health-retries 10 --health-start-period 30s steps: - name: Checkout repository uses: actions/checkout@v4 - name: Wait for service to be healthy run: | echo "⏳ Waiting for ConvertX service..." for i in {1..30}; do if curl -f http://localhost:3000/healthcheck 2>/dev/null; then echo "✅ Service is healthy!" break fi echo "Waiting... ($i/30)" sleep 5 done - name: Run API health check run: | echo "🔍 Testing API endpoints..." # Health check curl -f http://localhost:3000/healthcheck echo "" # Check if main page loads curl -f -o /dev/null -w "%{http_code}" http://localhost:3000/ echo "" echo "✅ API health checks passed!" # ============================================ # Job 3: Run comprehensive E2E tests inside container # ============================================ comprehensive-e2e: name: Comprehensive E2E Tests runs-on: ubuntu-24.04 needs: build-test-image timeout-minutes: 60 steps: - name: Checkout repository uses: actions/checkout@v4 - name: Free disk space (aggressive) uses: jlumbroso/free-disk-space@main with: tool-cache: true android: true dotnet: true haskell: true large-packages: true docker-images: true swap-storage: false - name: Login to GitHub Container Registry uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Pull test image run: docker pull ${{ needs.build-test-image.outputs.image }} - name: Run E2E tests inside Docker container run: | echo "🧪 Running comprehensive E2E tests inside Docker..." # 確保輸出目錄存在 mkdir -p "${{ github.workspace }}/test-results" docker run --rm \ --name convertx-e2e-test \ -v "${{ github.workspace }}/tests:/app/tests" \ -v "${{ github.workspace }}/test-results:/app/test-results" \ -e CI=true \ -e NODE_ENV=test \ --entrypoint /bin/bash \ ${{ needs.build-test-image.outputs.image }} \ -c " echo '📋 系統資訊 System info:' uname -a echo '' echo '🔧 可用轉換工具 Available conversion tools:' echo ' inkscape:' \$(inkscape --version 2>/dev/null | head -1 || echo '未找到 not found') echo ' pandoc:' \$(pandoc --version 2>/dev/null | head -1 || echo '未找到 not found') echo ' ffmpeg:' \$(ffmpeg -version 2>/dev/null | head -1 || echo '未找到 not found') echo ' libreoffice:' \$(soffice --version 2>/dev/null || echo '未找到 not found') echo ' imagemagick:' \$(magick --version 2>/dev/null | head -1 || echo '未找到 not found') echo ' calibre:' \$(ebook-convert --version 2>/dev/null | head -1 || echo '未找到 not found') echo ' potrace:' \$(potrace -v 2>/dev/null | head -1 || echo '未找到 not found') echo ' dasel:' \$(dasel --version 2>/dev/null || echo '未找到 not found') echo ' resvg:' \$(resvg --version 2>/dev/null || echo '未找到 not found') echo ' vips:' \$(vips --version 2>/dev/null || echo '未找到 not found') echo ' pdf2zh:' \$(pdf2zh --version 2>/dev/null || echo '未找到 not found') echo ' babeldoc:' \$(babeldoc --version 2>/dev/null || echo '未找到 not found') echo '' echo '📦 安裝測試依賴 Installing test dependencies...' cd /app bun install --frozen-lockfile || bun install echo '' echo '🧪 執行 E2E 測試 Running E2E tests...' # 執行綜合測試(允許失敗) bun test tests/e2e/comprehensive.e2e.test.ts --timeout 600000 || echo '⚠️ 綜合測試有部分失敗' # 執行格式矩陣測試(允許失敗) bun test tests/e2e/format-matrix.e2e.test.ts --timeout 300000 || echo '⚠️ 格式矩陣測試有部分失敗' # 執行基礎轉換器測試(允許失敗) bun test tests/e2e/converters.e2e.test.ts --timeout 120000 || echo '⚠️ 轉換器測試有部分失敗' echo '' echo '✅ E2E 測試完成!' # 複製結果到輸出目錄 mkdir -p /app/test-results cp -r tests/e2e/output/* /app/test-results/ 2>/dev/null || echo '⚠️ 無測試輸出可複製' ls -la /app/test-results/ 2>/dev/null || echo '📁 測試結果目錄為空' " # 檢查輸出目錄內容 echo "📁 測試結果目錄內容:" ls -la "${{ github.workspace }}/test-results/" || echo "目錄為空或不存在" - name: Create placeholder if no results if: always() run: | mkdir -p test-results if [ -z "$(ls -A test-results 2>/dev/null)" ]; then echo '{"message": "無測試結果輸出", "timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' > test-results/placeholder.json fi - name: Upload test results uses: actions/upload-artifact@v4 if: always() with: name: e2e-test-results path: test-results/ retention-days: 7 if-no-files-found: warn # ============================================ # Job 4: Translation tests (使用免費翻譯服務) # 使用 Google/Bing 免費翻譯,不需要付費 API 金鑰 # ============================================ translation-tests: name: Translation Tests (Free Services) runs-on: ubuntu-24.04 needs: build-test-image if: ${{ github.event.inputs.run_translation_tests == 'true' || github.event_name == 'push' }} timeout-minutes: 30 steps: - name: Checkout repository uses: actions/checkout@v4 - name: Free disk space (aggressive) uses: jlumbroso/free-disk-space@main with: tool-cache: true android: true dotnet: true haskell: true large-packages: true docker-images: true swap-storage: false - name: Login to GitHub Container Registry uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Pull test image run: docker pull ${{ needs.build-test-image.outputs.image }} - name: Run translation tests run: | echo "🌍 執行翻譯測試(使用免費服務:Google/Bing)..." # 確保輸出目錄存在 mkdir -p "${{ github.workspace }}/translation-results" docker run --rm \ --name convertx-translation-test \ -v "${{ github.workspace }}/tests:/app/tests" \ -v "${{ github.workspace }}/translation-results:/app/translation-results" \ -e CI=true \ -e PDFMATHTRANSLATE_SERVICE=google \ -e BABELDOC_SERVICE=google \ --entrypoint /bin/bash \ ${{ needs.build-test-image.outputs.image }} \ -c " cd /app bun install --frozen-lockfile || bun install echo '🌍 使用免費服務執行翻譯測試...' bun test tests/e2e/translation.e2e.test.ts --timeout 600000 || echo '⚠️ 翻譯測試有部分失敗' mkdir -p /app/translation-results cp -r tests/e2e/output/translation/* /app/translation-results/ 2>/dev/null || echo '⚠️ 無翻譯輸出可複製' ls -la /app/translation-results/ 2>/dev/null || echo '📁 翻譯結果目錄為空' " # 檢查輸出目錄內容 echo "📁 翻譯結果目錄內容:" ls -la "${{ github.workspace }}/translation-results/" || echo "目錄為空或不存在" - name: Create placeholder if no results if: always() run: | mkdir -p translation-results if [ -z "$(ls -A translation-results 2>/dev/null)" ]; then echo '{"message": "無翻譯結果輸出", "timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' > translation-results/placeholder.json fi - name: Upload translation results uses: actions/upload-artifact@v4 if: always() with: name: translation-test-results path: translation-results/ retention-days: 7 if-no-files-found: warn # ============================================ # Job 5: Cleanup test image # ============================================ cleanup: name: Cleanup Test Image runs-on: ubuntu-24.04 needs: [e2e-tests, comprehensive-e2e] if: always() permissions: packages: write steps: - name: Delete test image uses: actions/github-script@v7 continue-on-error: true with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const tag = `test-${{ github.sha }}`; console.log(`🗑️ Attempting to delete test image with tag: ${tag}`); // Note: This requires ghcr.io API access which may need additional setup // For now, images will be cleaned up by retention policies console.log('Test images will be cleaned up by retention policies'); # ============================================ # Job 6: Test summary # ============================================ summary: name: Test Summary runs-on: ubuntu-24.04 needs: [e2e-tests, comprehensive-e2e] if: always() steps: - name: Download test results uses: actions/download-artifact@v4 with: pattern: "*-test-results" path: test-results/ merge-multiple: true continue-on-error: true - name: Check downloaded results run: | echo "📁 已下載的測試結果:" ls -la test-results/ 2>/dev/null || echo "無測試結果可下載" - name: Generate summary run: | echo "# 🧪 Docker E2E 測試結果" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "## 測試狀態" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "| 任務 | 狀態 |" >> $GITHUB_STEP_SUMMARY echo "|------|------|" >> $GITHUB_STEP_SUMMARY echo "| E2E 測試 | ${{ needs.e2e-tests.result }} |" >> $GITHUB_STEP_SUMMARY echo "| 綜合 E2E 測試 | ${{ needs.comprehensive-e2e.result }} |" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY if [ -f "test-results/format-matrix/matrix-summary.md" ]; then echo "## 格式矩陣摘要" >> $GITHUB_STEP_SUMMARY cat test-results/format-matrix/matrix-summary.md >> $GITHUB_STEP_SUMMARY fi if [ -f "test-results/comprehensive/conversion-matrix.json" ]; then echo "" >> $GITHUB_STEP_SUMMARY echo "## 轉換矩陣" >> $GITHUB_STEP_SUMMARY echo '```json' >> $GITHUB_STEP_SUMMARY head -50 test-results/comprehensive/conversion-matrix.json >> $GITHUB_STEP_SUMMARY echo '```' >> $GITHUB_STEP_SUMMARY fi echo "" >> $GITHUB_STEP_SUMMARY echo "📅 測試完成時間: $(date -u '+%Y-%m-%d %H:%M:%S UTC')" >> $GITHUB_STEP_SUMMARY