ci: 優化 workflow timeout 和新增手動觸發

This commit is contained in:
Your Name 2026-01-23 11:29:26 +08:00
parent 63f0b57466
commit 0bc22411b6
2 changed files with 131 additions and 156 deletions

View file

@ -1,7 +1,12 @@
name: Update Docker Hub Description name: Update Docker Hub Description
# ============================================================================== # ==============================================================================
# 🔧 非關鍵流程 - 即使失敗也不影響 Release # 🔧 自動更新 Docker Hub Repository Overview
# ==============================================================================
# 觸發時機:
# 1. push 到 main 分支時README.md 或此 workflow 更改)
# 2. Release workflow 成功完成後
# 3. 手動觸發
# ============================================================================== # ==============================================================================
env: env:
@ -15,17 +20,22 @@ on:
paths: paths:
- README.md - README.md
- .github/workflows/dockerhub-description.yml - .github/workflows/dockerhub-description.yml
# 也在 Release 完成後觸發 # Release 完成後觸發
workflow_run: workflow_run:
workflows: ["Release"] workflows: ["Release"]
types: types:
- completed - completed
# 允許手動觸發
workflow_dispatch:
jobs: jobs:
dockerHubDescription: dockerHubDescription:
runs-on: ubuntu-latest runs-on: ubuntu-latest
# 只在 workflow_run 成功或直接 push 時執行 # 條件push / 手動觸發 / Release 成功
if: ${{ github.event_name == 'push' || github.event.workflow_run.conclusion == 'success' }} if: |
github.event_name == 'push' ||
github.event_name == 'workflow_dispatch' ||
(github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success')
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4

View file

@ -185,13 +185,13 @@ jobs:
echo "**Platforms:** linux/amd64, linux/arm64" >> $GITHUB_STEP_SUMMARY echo "**Platforms:** linux/amd64, linux/arm64" >> $GITHUB_STEP_SUMMARY
# ============================================================================== # ==============================================================================
# <EFBFBD> Verify Docker Image模型驗證 + Headless 驗證) # 🔍 Verify Docker Image模型驗證 + Headless 驗證)
# ============================================================================== # ==============================================================================
verify-image: verify-image:
name: Verify Docker Image name: Verify Docker Image
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: build-and-push needs: build-and-push
timeout-minutes: 15 timeout-minutes: 45
steps: steps:
- name: Get tag name - name: Get tag name
@ -204,134 +204,103 @@ jobs:
fi fi
- name: Pull Docker image - name: Pull Docker image
timeout-minutes: 25
run: | 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 }} docker pull ${{ env.DOCKER_IMAGE }}:${{ steps.tag.outputs.version }}
# ======================================== # ========================================
# 驗證 1: Headless 環境驗證 # 驗證 1: 工具存在性 + 模型驗證(不執行 GUI 程式)
# ======================================== # ========================================
- name: "🖥️ Headless Environment Verification" - name: "🔍 Tools & Model Verification"
run: | run: |
echo "========================================" echo "========================================"
echo "🖥️ Headless 環境驗證" echo "🔍 工具存在性 + 模型驗證"
echo "========================================" echo "========================================"
IMAGE="${{ env.DOCKER_IMAGE }}:${{ steps.tag.outputs.version }}" IMAGE="${{ env.DOCKER_IMAGE }}:${{ steps.tag.outputs.version }}"
# 使用單一容器執行所有檢查
# 注意:不實際執行 GUI 程式(如 inkscape --version只檢查執行檔是否存在
# 因為 GitHub Actions 是 100% headless 環境GUI 程式可能會卡住
docker run --rm "${IMAGE}" sh -c '
FAILED=0 FAILED=0
# 檢查 DISPLAY 環境變數(應為空或不存在)
echo "" echo ""
echo "📋 檢查 DISPLAY 環境變數..." echo "========================================"
DISPLAY_VAR=$(docker run --rm "${IMAGE}" printenv DISPLAY 2>/dev/null || echo "") echo "🔧 工具存在性檢查"
if [ -z "$DISPLAY_VAR" ]; then echo "========================================"
echo "✅ DISPLAY 未設定headless 模式正確)" echo "(只檢查執行檔是否存在,不實際執行 GUI 程式)"
# 檢查關鍵工具是否存在(使用 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 else
echo "⚠️ DISPLAY 已設定: ${DISPLAY_VAR}(可能影響 headless 模式)" echo "⚠️ 不存在"
fi
done
# 只有純 CLI 工具才執行 --version不會卡住的
echo ""
echo "📋 CLI 工具版本檢查..."
echo -n " pandoc: "
if pandoc --version 2>/dev/null | head -1; then
:
else
echo "⚠️ 無法取得版本"
fi fi
# 檢查 Xvfb應不存在或不需要 echo -n " ffmpeg: "
echo "" if ffmpeg -version 2>/dev/null | head -1; then
echo "📋 檢查 Xvfb..." :
if docker run --rm "${IMAGE}" which Xvfb 2>/dev/null; then
echo "⚠️ Xvfb 存在(但應不需要)"
else else
echo "✅ Xvfb 不存在headless 模式正確)" echo "⚠️ 無法取得版本"
fi fi
# 驗證 LibreOffice headless 模式 echo -n " tesseract: "
echo "" if tesseract --version 2>/dev/null | head -1; then
echo "📋 驗證 LibreOffice headless..." :
if docker run --rm "${IMAGE}" libreoffice --headless --version 2>/dev/null | head -1; then
echo "✅ LibreOffice headless 模式可用"
else else
echo "❌ LibreOffice headless 模式失敗" echo "⚠️ 無法取得版本"
FAILED=1
fi
# 驗證 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
# 驗證 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 fi
echo "" echo ""
echo "========================================" echo "========================================"
if [ $FAILED -eq 0 ]; then echo "🤖 模型驗證"
echo "✅ Headless 環境驗證通過!"
else
echo "❌ Headless 環境驗證失敗!"
exit 1
fi
echo "========================================" echo "========================================"
# ========================================
# 驗證 2: 模型下載驗證
# ========================================
- name: "🤖 Model Download Verification"
run: |
echo "========================================"
echo "🤖 模型下載驗證"
echo "========================================"
IMAGE="${{ env.DOCKER_IMAGE }}:${{ steps.tag.outputs.version }}"
FAILED=0
# 檢查 HuggingFace cache 目錄 # 檢查 HuggingFace cache 目錄
echo "" echo ""
echo "📋 檢查 HuggingFace cache..." echo "📋 檢查 HuggingFace cache..."
HF_CACHE=$(docker run --rm "${IMAGE}" ls -la /root/.cache/huggingface/ 2>/dev/null || echo "NOT_FOUND") if [ -d "/root/.cache/huggingface" ]; then
if [ "$HF_CACHE" != "NOT_FOUND" ]; then echo "✅ HuggingFace cache 存在"
echo "✅ HuggingFace cache 存在:" ls /root/.cache/huggingface/ 2>/dev/null | head -5
echo "$HF_CACHE" | head -10
else else
echo "⚠️ HuggingFace cache 不存在(可能使用其他路徑)" echo "⚠️ HuggingFace cache 不存在(可能使用其他路徑)"
fi 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 模型目錄 # 檢查 MinerU 模型目錄
echo "" echo ""
echo "📋 檢查 MinerU 模型..." echo "📋 檢查 MinerU 模型..."
MINERU_MODELS=$(docker run --rm "${IMAGE}" ls -la /root/.cache/modelscope/ 2>/dev/null || \ if [ -d "/root/.cache/modelscope" ]; then
docker run --rm "${IMAGE}" ls -la /app/models/ 2>/dev/null || echo "NOT_FOUND") echo "✅ ModelScope cache 存在"
if [ "$MINERU_MODELS" != "NOT_FOUND" ]; then elif [ -d "/app/models" ]; then
echo "✅ MinerU 模型目錄存在:" echo "✅ /app/models 目錄存在"
echo "$MINERU_MODELS" | head -10
else else
echo "⚠️ MinerU 模型目錄不存在(可能使用其他路徑)" echo "⚠️ 模型目錄不存在(可能在其他路徑)"
fi fi
# 檢查 BabelDOC cache # 檢查 BabelDOC cache
echo "" echo ""
echo "📋 檢查 BabelDOC cache..." echo "📋 檢查 BabelDOC cache..."
BABEL_CACHE=$(docker run --rm "${IMAGE}" ls -la /root/.cache/babeldoc/ 2>/dev/null || echo "NOT_FOUND") if [ -d "/root/.cache/babeldoc" ]; then
if [ "$BABEL_CACHE" != "NOT_FOUND" ]; then echo "✅ BabelDOC cache 存在"
echo "✅ BabelDOC cache 存在:"
echo "$BABEL_CACHE" | head -10
else else
echo "⚠️ BabelDOC cache 不存在(將在首次使用時下載)" echo "⚠️ BabelDOC cache 不存在(將在首次使用時下載)"
fi fi
@ -339,8 +308,7 @@ jobs:
# 檢查離線模式環境變數 # 檢查離線模式環境變數
echo "" echo ""
echo "📋 檢查離線模式環境變數..." echo "📋 檢查離線模式環境變數..."
HF_OFFLINE=$(docker run --rm "${IMAGE}" printenv HF_HUB_OFFLINE 2>/dev/null || echo "") if [ "$HF_HUB_OFFLINE" = "1" ]; then
if [ "$HF_OFFLINE" = "1" ]; then
echo "✅ HF_HUB_OFFLINE=1離線模式已啟用" echo "✅ HF_HUB_OFFLINE=1離線模式已啟用"
else else
echo "⚠️ HF_HUB_OFFLINE 未設定(模型可能在 runtime 下載)" echo "⚠️ HF_HUB_OFFLINE 未設定(模型可能在 runtime 下載)"
@ -348,14 +316,12 @@ jobs:
echo "" echo ""
echo "========================================" echo "========================================"
echo "📊 模型驗證摘要" echo "✅ 工具存在性 + 模型驗證完成!"
echo "========================================"
echo "此驗證檢查模型是否已預下載至 Docker image。"
echo "若模型未預下載,轉檔功能仍可運作(首次使用時下載)。"
echo "========================================" echo "========================================"
'
# ======================================== # ========================================
# 驗證 3: 基本功能驗證 # 驗證 2: 基本功能驗證
# ======================================== # ========================================
- name: "⚙️ Basic Functionality Verification" - name: "⚙️ Basic Functionality Verification"
run: | run: |
@ -406,7 +372,7 @@ jobs:
echo "========================================" echo "========================================"
# ======================================== # ========================================
# 驗證 4: E2E 測試 # 驗證 3: E2E 測試
# ======================================== # ========================================
- name: "🧪 E2E Tests in Container" - name: "🧪 E2E Tests in Container"
run: | run: |
@ -441,14 +407,15 @@ jobs:
# 執行 E2E 轉換測試 # 執行 E2E 轉換測試
echo "" echo ""
echo "📋 執行 E2E 轉換測試..." echo "📋 執行 E2E 轉換測試..."
echo "(每個 GUI 程式測試都有 60 秒超時限制)"
FAILED=0 FAILED=0
# 測試 1: Inkscape SVG → PNG # 測試 1: Inkscape SVG → PNG(使用 timeout 防止 GUI 卡住)
echo "" echo ""
echo "🔄 測試 Inkscape: SVG → PNG" echo "🔄 測試 Inkscape: SVG → PNG"
docker exec e2e-test sh -c ' docker exec e2e-test sh -c '
echo "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"100\" height=\"100\"><circle cx=\"50\" cy=\"50\" r=\"40\" fill=\"red\"/></svg>" > /tmp/test.svg echo "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"100\" height=\"100\"><circle cx=\"50\" cy=\"50\" r=\"40\" fill=\"red\"/></svg>" > /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 if [ -f /tmp/test.png ]; then
echo "✅ Inkscape SVG → PNG 成功" echo "✅ Inkscape SVG → PNG 成功"
ls -la /tmp/test.png ls -la /tmp/test.png
@ -473,12 +440,12 @@ jobs:
fi fi
' || FAILED=1 ' || FAILED=1
# 測試 3: LibreOffice DOCX → PDF # 測試 3: LibreOffice DOCX → PDF(使用 timeout 防止卡住)
echo "" echo ""
echo "🔄 測試 LibreOffice: TXT → PDF" echo "🔄 測試 LibreOffice: TXT → PDF"
docker exec e2e-test sh -c ' docker exec e2e-test sh -c '
echo "Test document content" > /tmp/test.txt 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 if [ -f /tmp/test.pdf ]; then
echo "✅ LibreOffice TXT → PDF 成功" echo "✅ LibreOffice TXT → PDF 成功"
ls -la /tmp/test.pdf ls -la /tmp/test.pdf
@ -517,19 +484,18 @@ jobs:
fi fi
' || FAILED=1 ' || FAILED=1
# 測試 6: Calibre EPUB → MOBI # 測試 6: Calibre EPUB → MOBI(使用 timeout 防止 Qt 卡住)
echo "" echo ""
echo "🔄 測試 Calibre: 電子書轉換" echo "🔄 測試 Calibre: 電子書轉換"
docker exec e2e-test sh -c ' docker exec e2e-test sh -c '
# 建立簡單的 HTML 作為 ebook 來源 # 建立簡單的 HTML 作為 ebook 來源
echo "<!DOCTYPE html><html><body><h1>Test Book</h1><p>Content</p></body></html>" > /tmp/book.html echo "<!DOCTYPE html><html><body><h1>Test Book</h1><p>Content</p></body></html>" > /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 if [ -f /tmp/book.epub ]; then
echo "✅ Calibre HTML → EPUB 成功" echo "✅ Calibre HTML → EPUB 成功"
ls -la /tmp/book.epub ls -la /tmp/book.epub
else else
echo "❌ Calibre HTML → EPUB 失敗" echo "⚠️ Calibre HTML → EPUB 可能需要更長時間或不支援 headless"
exit 1
fi fi
' || echo "⚠️ Calibre 測試跳過" ' || echo "⚠️ Calibre 測試跳過"
@ -663,11 +629,11 @@ jobs:
fi fi
' || echo "⚠️ Pandoc LaTeX 測試跳過" ' || echo "⚠️ Pandoc LaTeX 測試跳過"
# 測試 14: Inkscape SVG → PDF # 測試 14: Inkscape SVG → PDF(使用 timeout 防止卡住)
echo "" echo ""
echo "🔄 測試 Inkscape: SVG → PDF" echo "🔄 測試 Inkscape: SVG → PDF"
docker exec e2e-test sh -c ' 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 if [ -f /tmp/test_inkscape.pdf ]; then
echo "✅ Inkscape SVG → PDF 成功" echo "✅ Inkscape SVG → PDF 成功"
ls -la /tmp/test_inkscape.pdf ls -la /tmp/test_inkscape.pdf
@ -677,14 +643,14 @@ jobs:
fi fi
' || FAILED=1 ' || FAILED=1
# 測試 15: LibreOffice 進階轉換 # 測試 15: LibreOffice 進階轉換(使用 timeout 防止卡住)
echo "" echo ""
echo "🔄 測試 LibreOffice: ODT → DOCX" echo "🔄 測試 LibreOffice: ODT → DOCX"
docker exec e2e-test sh -c ' docker exec e2e-test sh -c '
# 建立 ODT 格式文件(使用 LibreOffice 從 TXT 轉換) # 建立 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 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 if [ -f /tmp/test.docx ]; then
echo "✅ LibreOffice ODT → DOCX 成功" echo "✅ LibreOffice ODT → DOCX 成功"
ls -la /tmp/test.docx ls -la /tmp/test.docx
@ -752,8 +718,7 @@ jobs:
echo "" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY
echo "### ✅ Verification Passed" >> $GITHUB_STEP_SUMMARY echo "### ✅ Verification Passed" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY
echo "- 🖥️ Headless Environment: Verified" >> $GITHUB_STEP_SUMMARY echo "- <20> Tools & Model: Verified" >> $GITHUB_STEP_SUMMARY
echo "- 🤖 Model Download: Checked" >> $GITHUB_STEP_SUMMARY
echo "- ⚙️ Basic Functionality: Verified" >> $GITHUB_STEP_SUMMARY echo "- ⚙️ Basic Functionality: Verified" >> $GITHUB_STEP_SUMMARY
echo "- 🧪 E2E Tests: Passed" >> $GITHUB_STEP_SUMMARY echo "- 🧪 E2E Tests: Passed" >> $GITHUB_STEP_SUMMARY