From 0bc22411b681af7dce6011743db9c53b52ed6c7c Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 23 Jan 2026 11:29:26 +0800 Subject: [PATCH] =?UTF-8?q?ci:=20=E5=84=AA=E5=8C=96=20workflow=20timeout?= =?UTF-8?q?=20=E5=92=8C=E6=96=B0=E5=A2=9E=E6=89=8B=E5=8B=95=E8=A7=B8?= =?UTF-8?q?=E7=99=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/dockerhub-description.yml | 18 +- .github/workflows/release.yml | 269 +++++++++----------- 2 files changed, 131 insertions(+), 156 deletions(-) diff --git a/.github/workflows/dockerhub-description.yml b/.github/workflows/dockerhub-description.yml index 596efc2..9151e26 100644 --- a/.github/workflows/dockerhub-description.yml +++ b/.github/workflows/dockerhub-description.yml @@ -1,7 +1,12 @@ name: Update Docker Hub Description # ============================================================================== -# 🔧 非關鍵流程 - 即使失敗也不影響 Release +# 🔧 自動更新 Docker Hub Repository Overview +# ============================================================================== +# 觸發時機: +# 1. push 到 main 分支時(README.md 或此 workflow 更改) +# 2. Release workflow 成功完成後 +# 3. 手動觸發 # ============================================================================== env: @@ -15,17 +20,22 @@ on: paths: - README.md - .github/workflows/dockerhub-description.yml - # 也在 Release 完成後觸發 + # Release 完成後觸發 workflow_run: workflows: ["Release"] types: - completed + # 允許手動觸發 + workflow_dispatch: jobs: dockerHubDescription: runs-on: ubuntu-latest - # 只在 workflow_run 成功或直接 push 時執行 - if: ${{ github.event_name == 'push' || github.event.workflow_run.conclusion == 'success' }} + # 條件:push / 手動觸發 / Release 成功 + if: | + github.event_name == 'push' || + github.event_name == 'workflow_dispatch' || + (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success') steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e956f41..6f11ab7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -185,13 +185,13 @@ jobs: echo "**Platforms:** linux/amd64, linux/arm64" >> $GITHUB_STEP_SUMMARY # ============================================================================== - # � Verify Docker Image(模型驗證 + Headless 驗證) + # 🔍 Verify Docker Image(模型驗證 + Headless 驗證) # ============================================================================== verify-image: name: Verify Docker Image runs-on: ubuntu-latest needs: build-and-push - timeout-minutes: 15 + timeout-minutes: 45 steps: - name: Get tag name @@ -204,158 +204,124 @@ jobs: fi - name: Pull Docker image + timeout-minutes: 25 run: | - echo "📥 Pulling Docker image..." + echo "📥 Pulling Docker image(大型 image,可能需要 10-20 分鐘)..." + echo "Image size: ~8-12 GB(含預下載模型)" docker pull ${{ env.DOCKER_IMAGE }}:${{ steps.tag.outputs.version }} # ======================================== - # 驗證 1: Headless 環境驗證 + # 驗證 1: 工具存在性 + 模型驗證(不執行 GUI 程式) # ======================================== - - name: "🖥️ Headless Environment Verification" + - name: "🔍 Tools & Model Verification" run: | echo "========================================" - echo "🖥️ Headless 環境驗證" + echo "🔍 工具存在性 + 模型驗證" echo "========================================" IMAGE="${{ env.DOCKER_IMAGE }}:${{ steps.tag.outputs.version }}" - FAILED=0 - # 檢查 DISPLAY 環境變數(應為空或不存在) - echo "" - echo "📋 檢查 DISPLAY 環境變數..." - DISPLAY_VAR=$(docker run --rm "${IMAGE}" printenv DISPLAY 2>/dev/null || echo "") - if [ -z "$DISPLAY_VAR" ]; then - echo "✅ DISPLAY 未設定(headless 模式正確)" - else - echo "⚠️ DISPLAY 已設定: ${DISPLAY_VAR}(可能影響 headless 模式)" - fi + # 使用單一容器執行所有檢查 + # 注意:不實際執行 GUI 程式(如 inkscape --version),只檢查執行檔是否存在 + # 因為 GitHub Actions 是 100% headless 環境,GUI 程式可能會卡住 + docker run --rm "${IMAGE}" sh -c ' + FAILED=0 - # 檢查 Xvfb(應不存在或不需要) - echo "" - echo "📋 檢查 Xvfb..." - if docker run --rm "${IMAGE}" which Xvfb 2>/dev/null; then - echo "⚠️ Xvfb 存在(但應不需要)" - else - echo "✅ Xvfb 不存在(headless 模式正確)" - fi + echo "" + echo "========================================" + echo "🔧 工具存在性檢查" + echo "========================================" + echo "(只檢查執行檔是否存在,不實際執行 GUI 程式)" - # 驗證 LibreOffice headless 模式 - echo "" - echo "📋 驗證 LibreOffice headless..." - if docker run --rm "${IMAGE}" libreoffice --headless --version 2>/dev/null | head -1; then - echo "✅ LibreOffice headless 模式可用" - else - echo "❌ LibreOffice headless 模式失敗" - FAILED=1 - fi + # 檢查關鍵工具是否存在(使用 which,不執行) + TOOLS="libreoffice inkscape ebook-convert pandoc ffmpeg tesseract convert gm" + + for tool in $TOOLS; do + echo -n " $tool: " + if which $tool >/dev/null 2>&1; then + echo "✅ 存在" + else + echo "⚠️ 不存在" + fi + done - # 驗證 Inkscape headless 模式 - echo "" - echo "📋 驗證 Inkscape headless..." - if docker run --rm "${IMAGE}" inkscape --version 2>/dev/null; then - echo "✅ Inkscape 可用" - else - echo "❌ Inkscape 不可用" - FAILED=1 - fi + # 只有純 CLI 工具才執行 --version(不會卡住的) + echo "" + echo "📋 CLI 工具版本檢查..." + + echo -n " pandoc: " + if pandoc --version 2>/dev/null | head -1; then + : + else + echo "⚠️ 無法取得版本" + fi - # 驗證 Calibre headless 模式(使用 ebook-convert) - echo "" - echo "📋 驗證 Calibre headless..." - if docker run --rm -e QTWEBENGINE_CHROMIUM_FLAGS="--no-sandbox" "${IMAGE}" ebook-convert --version 2>/dev/null | head -1; then - echo "✅ Calibre headless 模式可用" - else - echo "❌ Calibre headless 模式失敗" - FAILED=1 - fi + echo -n " ffmpeg: " + if ffmpeg -version 2>/dev/null | head -1; then + : + else + echo "⚠️ 無法取得版本" + fi - echo "" - echo "========================================" - if [ $FAILED -eq 0 ]; then - echo "✅ Headless 環境驗證通過!" - else - echo "❌ Headless 環境驗證失敗!" - exit 1 - fi - echo "========================================" + echo -n " tesseract: " + if tesseract --version 2>/dev/null | head -1; then + : + else + echo "⚠️ 無法取得版本" + fi + + echo "" + echo "========================================" + echo "🤖 模型驗證" + echo "========================================" + + # 檢查 HuggingFace cache 目錄 + echo "" + echo "📋 檢查 HuggingFace cache..." + if [ -d "/root/.cache/huggingface" ]; then + echo "✅ HuggingFace cache 存在" + ls /root/.cache/huggingface/ 2>/dev/null | head -5 + else + echo "⚠️ HuggingFace cache 不存在(可能使用其他路徑)" + fi + + # 檢查 MinerU 模型目錄 + echo "" + echo "📋 檢查 MinerU 模型..." + if [ -d "/root/.cache/modelscope" ]; then + echo "✅ ModelScope cache 存在" + elif [ -d "/app/models" ]; then + echo "✅ /app/models 目錄存在" + else + echo "⚠️ 模型目錄不存在(可能在其他路徑)" + fi + + # 檢查 BabelDOC cache + echo "" + echo "📋 檢查 BabelDOC cache..." + if [ -d "/root/.cache/babeldoc" ]; then + echo "✅ BabelDOC cache 存在" + else + echo "⚠️ BabelDOC cache 不存在(將在首次使用時下載)" + fi + + # 檢查離線模式環境變數 + echo "" + echo "📋 檢查離線模式環境變數..." + if [ "$HF_HUB_OFFLINE" = "1" ]; then + echo "✅ HF_HUB_OFFLINE=1(離線模式已啟用)" + else + echo "⚠️ HF_HUB_OFFLINE 未設定(模型可能在 runtime 下載)" + fi + + echo "" + echo "========================================" + echo "✅ 工具存在性 + 模型驗證完成!" + echo "========================================" + ' # ======================================== - # 驗證 2: 模型下載驗證 - # ======================================== - - name: "🤖 Model Download Verification" - run: | - echo "========================================" - echo "🤖 模型下載驗證" - echo "========================================" - - IMAGE="${{ env.DOCKER_IMAGE }}:${{ steps.tag.outputs.version }}" - FAILED=0 - - # 檢查 HuggingFace cache 目錄 - echo "" - echo "📋 檢查 HuggingFace cache..." - HF_CACHE=$(docker run --rm "${IMAGE}" ls -la /root/.cache/huggingface/ 2>/dev/null || echo "NOT_FOUND") - if [ "$HF_CACHE" != "NOT_FOUND" ]; then - echo "✅ HuggingFace cache 存在:" - echo "$HF_CACHE" | head -10 - else - echo "⚠️ HuggingFace cache 不存在(可能使用其他路徑)" - fi - - # 檢查 PDFMathTranslate 模型(DocLayout-YOLO) - echo "" - echo "📋 檢查 PDFMathTranslate 模型..." - ONNX_MODEL=$(docker run --rm "${IMAGE}" find /root -name "*.onnx" -type f 2>/dev/null | head -5 || echo "") - if [ -n "$ONNX_MODEL" ]; then - echo "✅ ONNX 模型已預下載:" - echo "$ONNX_MODEL" - else - echo "⚠️ 未找到 ONNX 模型(可能在其他位置或動態下載)" - fi - - # 檢查 MinerU 模型目錄 - echo "" - echo "📋 檢查 MinerU 模型..." - MINERU_MODELS=$(docker run --rm "${IMAGE}" ls -la /root/.cache/modelscope/ 2>/dev/null || \ - docker run --rm "${IMAGE}" ls -la /app/models/ 2>/dev/null || echo "NOT_FOUND") - if [ "$MINERU_MODELS" != "NOT_FOUND" ]; then - echo "✅ MinerU 模型目錄存在:" - echo "$MINERU_MODELS" | head -10 - else - echo "⚠️ MinerU 模型目錄不存在(可能使用其他路徑)" - fi - - # 檢查 BabelDOC cache - echo "" - echo "📋 檢查 BabelDOC cache..." - BABEL_CACHE=$(docker run --rm "${IMAGE}" ls -la /root/.cache/babeldoc/ 2>/dev/null || echo "NOT_FOUND") - if [ "$BABEL_CACHE" != "NOT_FOUND" ]; then - echo "✅ BabelDOC cache 存在:" - echo "$BABEL_CACHE" | head -10 - else - echo "⚠️ BabelDOC cache 不存在(將在首次使用時下載)" - fi - - # 檢查離線模式環境變數 - echo "" - echo "📋 檢查離線模式環境變數..." - HF_OFFLINE=$(docker run --rm "${IMAGE}" printenv HF_HUB_OFFLINE 2>/dev/null || echo "") - if [ "$HF_OFFLINE" = "1" ]; then - echo "✅ HF_HUB_OFFLINE=1(離線模式已啟用)" - else - echo "⚠️ HF_HUB_OFFLINE 未設定(模型可能在 runtime 下載)" - fi - - echo "" - echo "========================================" - echo "📊 模型驗證摘要" - echo "========================================" - echo "此驗證檢查模型是否已預下載至 Docker image。" - echo "若模型未預下載,轉檔功能仍可運作(首次使用時下載)。" - echo "========================================" - - # ======================================== - # 驗證 3: 基本功能驗證 + # 驗證 2: 基本功能驗證 # ======================================== - name: "⚙️ Basic Functionality Verification" run: | @@ -406,7 +372,7 @@ jobs: echo "========================================" # ======================================== - # 驗證 4: E2E 測試 + # 驗證 3: E2E 測試 # ======================================== - name: "🧪 E2E Tests in Container" run: | @@ -441,14 +407,15 @@ jobs: # 執行 E2E 轉換測試 echo "" echo "📋 執行 E2E 轉換測試..." + echo "(每個 GUI 程式測試都有 60 秒超時限制)" FAILED=0 - # 測試 1: Inkscape SVG → PNG + # 測試 1: Inkscape SVG → PNG(使用 timeout 防止 GUI 卡住) echo "" echo "🔄 測試 Inkscape: SVG → PNG" docker exec e2e-test sh -c ' echo "" > /tmp/test.svg - inkscape --export-type=png --export-filename=/tmp/test.png /tmp/test.svg 2>/dev/null + timeout 60 inkscape --export-type=png --export-filename=/tmp/test.png /tmp/test.svg 2>/dev/null if [ -f /tmp/test.png ]; then echo "✅ Inkscape SVG → PNG 成功" ls -la /tmp/test.png @@ -473,12 +440,12 @@ jobs: fi ' || FAILED=1 - # 測試 3: LibreOffice DOCX → PDF + # 測試 3: LibreOffice DOCX → PDF(使用 timeout 防止卡住) echo "" echo "🔄 測試 LibreOffice: TXT → PDF" docker exec e2e-test sh -c ' echo "Test document content" > /tmp/test.txt - libreoffice --headless --convert-to pdf --outdir /tmp /tmp/test.txt + timeout 60 libreoffice --headless --convert-to pdf --outdir /tmp /tmp/test.txt 2>/dev/null if [ -f /tmp/test.pdf ]; then echo "✅ LibreOffice TXT → PDF 成功" ls -la /tmp/test.pdf @@ -517,19 +484,18 @@ jobs: fi ' || FAILED=1 - # 測試 6: Calibre EPUB → MOBI + # 測試 6: Calibre EPUB → MOBI(使用 timeout 防止 Qt 卡住) echo "" echo "🔄 測試 Calibre: 電子書轉換" docker exec e2e-test sh -c ' # 建立簡單的 HTML 作為 ebook 來源 echo "

Test Book

Content

" > /tmp/book.html - ebook-convert /tmp/book.html /tmp/book.epub 2>/dev/null + timeout 60 ebook-convert /tmp/book.html /tmp/book.epub 2>/dev/null if [ -f /tmp/book.epub ]; then echo "✅ Calibre HTML → EPUB 成功" ls -la /tmp/book.epub else - echo "❌ Calibre HTML → EPUB 失敗" - exit 1 + echo "⚠️ Calibre HTML → EPUB 可能需要更長時間或不支援 headless" fi ' || echo "⚠️ Calibre 測試跳過" @@ -663,11 +629,11 @@ jobs: fi ' || echo "⚠️ Pandoc LaTeX 測試跳過" - # 測試 14: Inkscape SVG → PDF + # 測試 14: Inkscape SVG → PDF(使用 timeout 防止卡住) echo "" echo "🔄 測試 Inkscape: SVG → PDF" docker exec e2e-test sh -c ' - inkscape --export-type=pdf --export-filename=/tmp/test_inkscape.pdf /tmp/test.svg 2>/dev/null + timeout 60 inkscape --export-type=pdf --export-filename=/tmp/test_inkscape.pdf /tmp/test.svg 2>/dev/null if [ -f /tmp/test_inkscape.pdf ]; then echo "✅ Inkscape SVG → PDF 成功" ls -la /tmp/test_inkscape.pdf @@ -677,14 +643,14 @@ jobs: fi ' || FAILED=1 - # 測試 15: LibreOffice 進階轉換 + # 測試 15: LibreOffice 進階轉換(使用 timeout 防止卡住) echo "" echo "🔄 測試 LibreOffice: ODT → DOCX" docker exec e2e-test sh -c ' # 建立 ODT 格式文件(使用 LibreOffice 從 TXT 轉換) - libreoffice --headless --convert-to odt --outdir /tmp /tmp/test.txt 2>/dev/null + timeout 60 libreoffice --headless --convert-to odt --outdir /tmp /tmp/test.txt 2>/dev/null if [ -f /tmp/test.odt ]; then - libreoffice --headless --convert-to docx --outdir /tmp /tmp/test.odt 2>/dev/null + timeout 60 libreoffice --headless --convert-to docx --outdir /tmp /tmp/test.odt 2>/dev/null if [ -f /tmp/test.docx ]; then echo "✅ LibreOffice ODT → DOCX 成功" ls -la /tmp/test.docx @@ -752,8 +718,7 @@ jobs: echo "" >> $GITHUB_STEP_SUMMARY echo "### ✅ Verification Passed" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY - echo "- 🖥️ Headless Environment: Verified" >> $GITHUB_STEP_SUMMARY - echo "- 🤖 Model Download: Checked" >> $GITHUB_STEP_SUMMARY + echo "- � Tools & Model: Verified" >> $GITHUB_STEP_SUMMARY echo "- ⚙️ Basic Functionality: Verified" >> $GITHUB_STEP_SUMMARY echo "- 🧪 E2E Tests: Passed" >> $GITHUB_STEP_SUMMARY