convertor/scripts/verify-installation.sh
Your Name 55b0f519ab refactor(docker): 完整重構 Dockerfile v0.1.16
BREAKING CHANGE: Dockerfile 結構重新設計

## 重構內容

### Multi-Stage Build 結構
- Stage 1 [base]: Bun runtime 基礎
- Stage 2 [install]: Node 依賴安裝
- Stage 3 [prerelease]: 應用程式建構
- Stage 4 [system-tools]: APT 系統工具(拆分為 14 個獨立 RUN)
- Stage 5 [fonts]: 字型安裝
- Stage 6 [python-tools]: Python CLI 工具(拆分為 9 個獨立 RUN)
- Stage 7 [models]: 模型下載
- Stage 8 [release]: 最終 Image

### 新增 Scripts
- install-system-tools.sh: 系統工具安裝
- install-fonts.sh: 字型安裝
- install-python-tools.sh: Python 工具安裝
- download-models.sh: 模型下載
- verify-installation.sh: 完整安裝驗證

### 刪除舊檔案
- Dockerfile.backup
- Dockerfile.offline
- Dockerfile.old

### 路徑更新
- MinerU 模型: /opt/convertx/models/mineru/PDF-Extract-Kit-1.0
- BabelDOC 快取: /root/.cache/babeldoc

### Multi-Arch 支援
- linux/amd64: 功能完整
- linux/arm64: 安全降級(MinerU/resvg 跳過)

Closes #docker-refactor
2026-01-25 13:15:31 +08:00

341 lines
10 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# ==============================================================================
# ConvertX-CN 安裝驗證腳本
# ==============================================================================
# 用途:在 Docker build 最終階段驗證所有工具和模型
# 執行環境Docker build stage (final) 或 runtime 驗證
# ==============================================================================
set -euo pipefail
# 顏色輸出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() { echo -e "${BLUE}🔍 [INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}✅ [SUCCESS]${NC} $1"; }
log_warn() { echo -e "${YELLOW}⚠️ [WARN]${NC} $1"; }
log_error() { echo -e "${RED}❌ [ERROR]${NC} $1"; }
# 架構檢測
ARCH=$(uname -m)
# 計數器
PASS=0
FAIL=0
WARN=0
SKIP=0
# ==============================================================================
# 檢查工具函數
# ==============================================================================
check_command() {
local cmd="$1"
local name="$2"
local required="${3:-true}"
local arch_only="${4:-all}"
# 架構檢查
if [ "${arch_only}" != "all" ] && [ "${ARCH}" != "${arch_only}" ]; then
echo " ⏭️ ${name}: 跳過(僅 ${arch_only}"
((SKIP++))
return 0
fi
if command -v "${cmd}" >/dev/null 2>&1; then
local path
path=$(which "${cmd}")
echo "${name}: ${path}"
((PASS++))
else
if [ "${required}" = "true" ]; then
echo "${name}: 未找到(必要)"
((FAIL++))
else
echo " ⚠️ ${name}: 未找到(可選)"
((WARN++))
fi
fi
}
check_file() {
local path="$1"
local name="$2"
local required="${3:-true}"
local arch_only="${4:-all}"
# 架構檢查
if [ "${arch_only}" != "all" ] && [ "${ARCH}" != "${arch_only}" ]; then
echo " ⏭️ ${name}: 跳過(僅 ${arch_only}"
((SKIP++))
return 0
fi
if [ -f "${path}" ]; then
local size
size=$(ls -lh "${path}" 2>/dev/null | awk '{print $5}')
echo "${name}: ${size}"
((PASS++))
else
if [ "${required}" = "true" ]; then
echo "${name}: 不存在"
((FAIL++))
else
echo " ⚠️ ${name}: 不存在(可選)"
((WARN++))
fi
fi
}
check_dir() {
local path="$1"
local name="$2"
local required="${3:-true}"
local arch_only="${4:-all}"
# 架構檢查
if [ "${arch_only}" != "all" ] && [ "${ARCH}" != "${arch_only}" ]; then
echo " ⏭️ ${name}: 跳過(僅 ${arch_only}"
((SKIP++))
return 0
fi
if [ -d "${path}" ]; then
local size
size=$(du -sh "${path}" 2>/dev/null | awk '{print $1}')
echo "${name}: ${size}"
((PASS++))
else
if [ "${required}" = "true" ]; then
echo "${name}: 不存在"
((FAIL++))
else
echo " ⚠️ ${name}: 不存在(可選)"
((WARN++))
fi
fi
}
# ==============================================================================
# 1. 核心系統工具
# ==============================================================================
verify_system_tools() {
echo ""
echo "📦 核心系統工具"
echo "=============================================="
check_command "ffmpeg" "FFmpeg"
check_command "convert" "ImageMagick"
check_command "gm" "GraphicsMagick"
check_command "vips" "libvips"
check_command "inkscape" "Inkscape"
check_command "resvg" "resvg" "false" "x86_64"
check_command "dvisvgm" "dvisvgm"
check_command "xelatex" "XeLaTeX"
check_command "soffice" "LibreOffice"
check_command "pandoc" "Pandoc"
check_command "ebook-convert" "Calibre"
check_command "assimp" "Assimp"
check_command "dasel" "dasel"
check_command "msgconvert" "msgconvert"
check_command "deark" "deark"
check_command "vtracer" "vtracer"
check_command "heif-convert" "libheif"
check_command "djxl" "libjxl"
check_command "ocrmypdf" "OCRmyPDF"
check_command "tesseract" "Tesseract"
check_command "potrace" "Potrace"
check_command "qpdf" "qpdf"
check_command "gs" "Ghostscript"
}
# ==============================================================================
# 2. Python 工具
# ==============================================================================
verify_python_tools() {
echo ""
echo "🐍 Python 工具"
echo "=============================================="
check_command "python3" "Python"
check_command "pip3" "pip"
check_command "uv" "uv"
check_command "markitdown" "markitdown"
check_command "pdf2zh" "PDFMathTranslate"
check_command "babeldoc" "BabelDOC"
check_command "mineru" "MinerU" "true" "x86_64"
}
# ==============================================================================
# 3. 模型檔案
# ==============================================================================
verify_models() {
echo ""
echo "🤖 AI 模型"
echo "=============================================="
# MinerU 模型
check_dir "/opt/convertx/models/mineru/PDF-Extract-Kit-1.0" "MinerU Pipeline" "true" "x86_64"
check_file "/root/mineru.json" "MinerU 配置檔" "true" "x86_64"
# BabelDOC 模型
check_file "/root/.cache/babeldoc/models/doclayout_yolo_docstructbench_imgsz1024.onnx" "DocLayout-YOLO ONNX" "false"
check_dir "/root/.cache/babeldoc" "BabelDOC 快取" "false"
}
# ==============================================================================
# 4. 字型
# ==============================================================================
verify_fonts() {
echo ""
echo "🔤 字型"
echo "=============================================="
check_file "/usr/share/fonts/truetype/custom/GoNotoKurrent-Regular.ttf" "Noto 通用字型"
check_file "/usr/share/fonts/truetype/custom/SourceHanSerifCN-Regular.ttf" "思源宋體(簡體)"
check_file "/usr/share/fonts/truetype/custom/SourceHanSerifTW-Regular.ttf" "思源宋體(繁體)"
check_file "/usr/share/fonts/truetype/custom/SourceHanSerifJP-Regular.ttf" "思源宋體(日文)"
check_file "/usr/share/fonts/truetype/custom/SourceHanSerifKR-Regular.ttf" "思源宋體(韓文)"
check_file "/usr/share/fonts/truetype/custom/BiauKai.ttf" "標楷體" "false"
# 系統字型
echo ""
echo " 系統字型統計:"
local noto_count
noto_count=$(fc-list | grep -ci "noto" || echo "0")
echo " - Noto 字型: ${noto_count}"
local total_count
total_count=$(fc-list | wc -l)
echo " - 總字型數: ${total_count}"
}
# ==============================================================================
# 5. OCR 語言
# ==============================================================================
verify_ocr_languages() {
echo ""
echo "📝 OCR 語言"
echo "=============================================="
local languages=("eng" "chi_tra" "chi_sim" "jpn" "kor" "deu" "fra")
for lang in "${languages[@]}"; do
if tesseract --list-langs 2>&1 | grep -q "^${lang}$"; then
echo "${lang}: 已安裝"
((PASS++))
else
echo "${lang}: 未安裝"
((FAIL++))
fi
done
}
# ==============================================================================
# 6. Locale 設定
# ==============================================================================
verify_locales() {
echo ""
echo "🌍 Locale 設定"
echo "=============================================="
local locales=("en_US.UTF-8" "zh_TW.UTF-8" "zh_CN.UTF-8" "ja_JP.UTF-8" "ko_KR.UTF-8")
for locale in "${locales[@]}"; do
if locale -a 2>/dev/null | grep -q "${locale}"; then
echo "${locale}: 已啟用"
((PASS++))
else
echo " ⚠️ ${locale}: 未啟用"
((WARN++))
fi
done
}
# ==============================================================================
# 7. PDF 簽章
# ==============================================================================
verify_pdf_signing() {
echo ""
echo "🔐 PDF 簽章"
echo "=============================================="
check_file "/app/certs/default.p12" "預設憑證" "false"
}
# ==============================================================================
# 8. 離線模式驗證
# ==============================================================================
verify_offline_mode() {
echo ""
echo "🔒 離線模式環境變數"
echo "=============================================="
local vars=(
"HF_HUB_OFFLINE"
"TRANSFORMERS_OFFLINE"
"MINERU_MODEL_SOURCE"
"BABELDOC_OFFLINE"
)
for var in "${vars[@]}"; do
local value="${!var:-}"
if [ -n "${value}" ]; then
echo "${var}=${value}"
((PASS++))
else
echo " ⚠️ ${var}: 未設定"
((WARN++))
fi
done
}
# ==============================================================================
# 總結
# ==============================================================================
print_summary() {
echo ""
echo "=============================================="
echo "📊 驗證總結"
echo "=============================================="
echo " ✅ 通過: ${PASS}"
echo " ❌ 失敗: ${FAIL}"
echo " ⚠️ 警告: ${WARN}"
echo " ⏭️ 跳過: ${SKIP}"
echo "=============================================="
if [ "${FAIL}" -gt 0 ]; then
log_error "驗證失敗!有 ${FAIL} 個必要項目未通過"
return 1
else
log_success "驗證通過!"
return 0
fi
}
# ==============================================================================
# 主程式
# ==============================================================================
main() {
log_info "=========================================="
log_info "ConvertX-CN 安裝驗證"
log_info "架構: ${ARCH}"
log_info "=========================================="
verify_system_tools
verify_python_tools
verify_models
verify_fonts
verify_ocr_languages
verify_locales
verify_pdf_signing
verify_offline_mode
print_summary
}
# 執行
main "$@"