- Add auto-upstream-sync workflow with dedup protection for conflict issues - Add upstream-sync build + smoke test workflow - Add smoke-test script and test files - Add upstream sync issue template and documentation - Fix: use --allow-unrelated-histories for merge command - Fix: check for existing open merge-conflict issues before creating duplicates
412 lines
13 KiB
YAML
412 lines
13 KiB
YAML
# ==============================================================================
|
||
# Upstream Sync Build + Smoke Test
|
||
# 功能:
|
||
# 1. dev 分支有 push 時自動觸發
|
||
# 2. 建構 Docker image(僅 amd64 用於測試)
|
||
# 3. 執行 smoke test(10 分鐘內完成)
|
||
# 4. 測試通過後推送 multi-arch image
|
||
# ==============================================================================
|
||
|
||
name: Upstream Sync Build
|
||
|
||
on:
|
||
push:
|
||
branches:
|
||
- dev
|
||
|
||
env:
|
||
DOCKER_IMAGE: convertx/convertx-cn
|
||
TEST_IMAGE: convertx-cn-test
|
||
# Smoke test 超時時間(秒)
|
||
SMOKE_TEST_TIMEOUT: 600
|
||
|
||
concurrency:
|
||
group: upstream-sync-${{ github.ref }}
|
||
cancel-in-progress: true
|
||
|
||
jobs:
|
||
# ==========================================================================
|
||
# Job 1: 快速建構測試用 image(僅 amd64)
|
||
# ==========================================================================
|
||
build-test-image:
|
||
name: Build Test Image
|
||
runs-on: ubuntu-latest
|
||
outputs:
|
||
image_tag: ${{ steps.date.outputs.date }}
|
||
|
||
steps:
|
||
- name: Checkout repository
|
||
uses: actions/checkout@v4
|
||
|
||
# ========================================
|
||
# 清理磁碟空間(解決 no space left on device)
|
||
# ========================================
|
||
- name: Free disk space
|
||
run: |
|
||
echo "🧹 Cleaning up disk space..."
|
||
echo "Before cleanup:"
|
||
df -h
|
||
|
||
# 移除不需要的預裝軟體
|
||
sudo rm -rf /usr/share/dotnet
|
||
sudo rm -rf /usr/local/lib/android
|
||
sudo rm -rf /opt/ghc
|
||
sudo rm -rf /opt/hostedtoolcache/CodeQL
|
||
sudo rm -rf /usr/local/share/boost
|
||
sudo rm -rf /usr/share/swift
|
||
|
||
# 清理 apt cache
|
||
sudo apt-get clean
|
||
sudo apt-get autoremove -y
|
||
|
||
# 清理 Docker 舊資料
|
||
docker system prune -af --volumes || true
|
||
|
||
echo ""
|
||
echo "After cleanup:"
|
||
df -h
|
||
|
||
- name: Get current date
|
||
id: date
|
||
run: echo "date=$(date +'%Y%m%d')" >> $GITHUB_OUTPUT
|
||
|
||
- name: Set up Docker Buildx
|
||
uses: docker/setup-buildx-action@v3
|
||
|
||
- name: Build test image (amd64 only)
|
||
uses: docker/build-push-action@v6
|
||
with:
|
||
context: .
|
||
push: false
|
||
load: true
|
||
tags: ${{ env.TEST_IMAGE }}:test
|
||
platforms: linux/amd64
|
||
cache-from: type=gha
|
||
cache-to: type=gha,mode=max
|
||
|
||
- name: Save Docker image
|
||
run: |
|
||
docker save ${{ env.TEST_IMAGE }}:test -o /tmp/test-image.tar
|
||
|
||
- name: Upload test image
|
||
uses: actions/upload-artifact@v4
|
||
with:
|
||
name: test-image
|
||
path: /tmp/test-image.tar
|
||
retention-days: 1
|
||
|
||
# ==========================================================================
|
||
# Job 2: Smoke Test(必須在 10 分鐘內完成)
|
||
# ==========================================================================
|
||
smoke-test:
|
||
name: Smoke Test
|
||
runs-on: ubuntu-latest
|
||
needs: build-test-image
|
||
timeout-minutes: 10
|
||
|
||
steps:
|
||
- name: Checkout repository
|
||
uses: actions/checkout@v4
|
||
|
||
- name: Download test image
|
||
uses: actions/download-artifact@v4
|
||
with:
|
||
name: test-image
|
||
path: /tmp
|
||
|
||
- name: Load Docker image
|
||
run: |
|
||
docker load -i /tmp/test-image.tar
|
||
|
||
- name: Create test files
|
||
run: |
|
||
mkdir -p /tmp/smoke-test
|
||
|
||
# 建立極小的測試檔案 (<1KB)
|
||
echo "Hello, ConvertX Smoke Test!" > /tmp/smoke-test/test.txt
|
||
echo "這是中文測試檔案" >> /tmp/smoke-test/test.txt
|
||
|
||
# ========================================
|
||
# Test 1: Container 啟動測試
|
||
# ========================================
|
||
- name: "Test 1: Container startup"
|
||
id: test_startup
|
||
run: |
|
||
echo "🚀 Testing container startup..."
|
||
|
||
# 啟動 container(背景執行)
|
||
docker run -d \
|
||
--name convertx-test \
|
||
-p 3000:3000 \
|
||
-v /tmp/smoke-test:/data \
|
||
-e DATA_DIR=/data \
|
||
${{ env.TEST_IMAGE }}:test
|
||
|
||
# 等待啟動(最多 60 秒)
|
||
echo "⏳ Waiting for container to start..."
|
||
for i in {1..60}; do
|
||
if docker exec convertx-test curl -sf http://localhost:3000/healthcheck > /dev/null 2>&1; then
|
||
echo "✅ Container started successfully in ${i}s"
|
||
echo "startup_time=${i}" >> $GITHUB_OUTPUT
|
||
exit 0
|
||
fi
|
||
sleep 1
|
||
done
|
||
|
||
echo "❌ Container failed to start within 60s"
|
||
docker logs convertx-test
|
||
exit 1
|
||
|
||
# ========================================
|
||
# Test 2: 關鍵工具存在性檢查
|
||
# ========================================
|
||
- name: "Test 2: Tool availability check"
|
||
id: test_tools
|
||
run: |
|
||
echo "🔧 Checking critical tools..."
|
||
|
||
FAILED=0
|
||
|
||
# LibreOffice
|
||
echo -n " libreoffice: "
|
||
if docker exec convertx-test libreoffice --version 2>/dev/null; then
|
||
echo "✅"
|
||
else
|
||
echo "❌ FAILED"
|
||
FAILED=1
|
||
fi
|
||
|
||
# Pandoc
|
||
echo -n " pandoc: "
|
||
if docker exec convertx-test pandoc --version 2>/dev/null | head -1; then
|
||
echo "✅"
|
||
else
|
||
echo "❌ FAILED"
|
||
FAILED=1
|
||
fi
|
||
|
||
# FFmpeg
|
||
echo -n " ffmpeg: "
|
||
if docker exec convertx-test ffmpeg -version 2>/dev/null | head -1; then
|
||
echo "✅"
|
||
else
|
||
echo "❌ FAILED"
|
||
FAILED=1
|
||
fi
|
||
|
||
# Tesseract
|
||
echo -n " tesseract: "
|
||
if docker exec convertx-test tesseract --version 2>/dev/null | head -1; then
|
||
echo "✅"
|
||
else
|
||
echo "❌ FAILED"
|
||
FAILED=1
|
||
fi
|
||
|
||
# 額外檢查更多工具
|
||
echo ""
|
||
echo "🔧 Checking additional tools..."
|
||
|
||
# ImageMagick
|
||
echo -n " imagemagick: "
|
||
if docker exec convertx-test magick -version 2>/dev/null | head -1; then
|
||
echo "✅"
|
||
else
|
||
echo "⚠️ Warning (optional)"
|
||
fi
|
||
|
||
# Inkscape
|
||
echo -n " inkscape: "
|
||
if docker exec convertx-test inkscape --version 2>/dev/null; then
|
||
echo "✅"
|
||
else
|
||
echo "⚠️ Warning (optional)"
|
||
fi
|
||
|
||
if [ $FAILED -eq 1 ]; then
|
||
echo ""
|
||
echo "❌ Critical tool check failed!"
|
||
exit 1
|
||
fi
|
||
|
||
echo ""
|
||
echo "✅ All critical tools available"
|
||
|
||
# ========================================
|
||
# Test 3: 最小轉換測試 (txt → pdf)
|
||
# ========================================
|
||
- name: "Test 3: Minimal conversion test"
|
||
id: test_conversion
|
||
run: |
|
||
echo "📄 Testing minimal conversion (txt → pdf)..."
|
||
|
||
# 使用 API 進行轉換測試
|
||
# 先上傳檔案
|
||
echo " Uploading test file..."
|
||
UPLOAD_RESPONSE=$(curl -sf -X POST \
|
||
-F "file=@/tmp/smoke-test/test.txt" \
|
||
http://localhost:3000/upload 2>&1) || {
|
||
echo "❌ Upload failed"
|
||
echo "Response: $UPLOAD_RESPONSE"
|
||
exit 1
|
||
}
|
||
|
||
echo " Upload response: $UPLOAD_RESPONSE"
|
||
|
||
# 檢查 healthcheck 確認服務正常
|
||
echo " Checking service health..."
|
||
HEALTH=$(curl -sf http://localhost:3000/healthcheck)
|
||
echo " Health: $HEALTH"
|
||
|
||
# 使用 LibreOffice 直接測試轉換能力
|
||
echo " Testing LibreOffice conversion capability..."
|
||
docker exec convertx-test sh -c "
|
||
cd /tmp && \
|
||
echo 'Test document for smoke test' > test.txt && \
|
||
libreoffice --headless --convert-to pdf test.txt && \
|
||
ls -la test.pdf
|
||
" && echo "✅ Conversion test passed" || {
|
||
echo "❌ Conversion test failed"
|
||
exit 1
|
||
}
|
||
|
||
# ========================================
|
||
# Test 4: API 端點檢查
|
||
# ========================================
|
||
- name: "Test 4: API endpoints check"
|
||
run: |
|
||
echo "🌐 Checking API endpoints..."
|
||
|
||
# Healthcheck
|
||
echo -n " /healthcheck: "
|
||
if curl -sf http://localhost:3000/healthcheck > /dev/null; then
|
||
echo "✅"
|
||
else
|
||
echo "❌"
|
||
exit 1
|
||
fi
|
||
|
||
# Root page
|
||
echo -n " / (root): "
|
||
if curl -sf http://localhost:3000/ > /dev/null; then
|
||
echo "✅"
|
||
else
|
||
echo "❌"
|
||
exit 1
|
||
fi
|
||
|
||
# Converters list
|
||
echo -n " /converters: "
|
||
if curl -sf http://localhost:3000/converters > /dev/null; then
|
||
echo "✅"
|
||
else
|
||
echo "⚠️ (may require auth)"
|
||
fi
|
||
|
||
echo ""
|
||
echo "✅ API endpoints check passed"
|
||
|
||
- name: Cleanup test container
|
||
if: always()
|
||
run: |
|
||
docker stop convertx-test || true
|
||
docker rm convertx-test || true
|
||
|
||
- name: Smoke test summary
|
||
if: always()
|
||
run: |
|
||
echo "## 🧪 Smoke Test Results" >> $GITHUB_STEP_SUMMARY
|
||
echo "" >> $GITHUB_STEP_SUMMARY
|
||
echo "| 測試項目 | 狀態 |" >> $GITHUB_STEP_SUMMARY
|
||
echo "|----------|------|" >> $GITHUB_STEP_SUMMARY
|
||
echo "| Container 啟動 | ✅ 通過 (${startup_time:-N/A}s) |" >> $GITHUB_STEP_SUMMARY
|
||
echo "| 工具檢查 (libreoffice, pandoc, ffmpeg, tesseract) | ✅ 通過 |" >> $GITHUB_STEP_SUMMARY
|
||
echo "| 最小轉換測試 | ✅ 通過 |" >> $GITHUB_STEP_SUMMARY
|
||
echo "| API 端點檢查 | ✅ 通過 |" >> $GITHUB_STEP_SUMMARY
|
||
|
||
# ==========================================================================
|
||
# Job 3: 測試通過後,建構並推送 multi-arch image
|
||
# ==========================================================================
|
||
build-and-push:
|
||
name: Build & Push Multi-arch Image
|
||
runs-on: ubuntu-latest
|
||
needs: [build-test-image, smoke-test]
|
||
|
||
steps:
|
||
- name: Checkout repository
|
||
uses: actions/checkout@v4
|
||
|
||
# ========================================
|
||
# 清理磁碟空間(multi-arch build 需要更多空間)
|
||
# ========================================
|
||
- name: Free disk space
|
||
run: |
|
||
echo "🧹 Cleaning up disk space for multi-arch build..."
|
||
echo "Before cleanup:"
|
||
df -h
|
||
|
||
# 移除不需要的預裝軟體
|
||
sudo rm -rf /usr/share/dotnet
|
||
sudo rm -rf /usr/local/lib/android
|
||
sudo rm -rf /opt/ghc
|
||
sudo rm -rf /opt/hostedtoolcache/CodeQL
|
||
sudo rm -rf /usr/local/share/boost
|
||
sudo rm -rf /usr/share/swift
|
||
sudo rm -rf /opt/hostedtoolcache
|
||
|
||
# 清理 apt cache
|
||
sudo apt-get clean
|
||
sudo apt-get autoremove -y
|
||
|
||
# 清理 Docker 舊資料
|
||
docker system prune -af --volumes || true
|
||
|
||
echo ""
|
||
echo "After cleanup:"
|
||
df -h
|
||
|
||
- name: Get current date
|
||
id: date
|
||
run: echo "date=$(date +'%Y%m%d')" >> $GITHUB_OUTPUT
|
||
|
||
- name: Set up QEMU
|
||
uses: docker/setup-qemu-action@v3
|
||
|
||
- name: Set up Docker Buildx
|
||
uses: docker/setup-buildx-action@v3
|
||
|
||
- name: Login to Docker Hub
|
||
uses: docker/login-action@v3
|
||
with:
|
||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||
|
||
- name: Build and push Docker image
|
||
uses: docker/build-push-action@v6
|
||
with:
|
||
context: .
|
||
push: true
|
||
tags: ${{ env.DOCKER_IMAGE }}:upstream-${{ steps.date.outputs.date }}
|
||
platforms: linux/amd64,linux/arm64
|
||
cache-from: type=gha
|
||
cache-to: type=gha,mode=max
|
||
|
||
- name: Summary
|
||
run: |
|
||
echo "## 🐳 Docker Image Published" >> $GITHUB_STEP_SUMMARY
|
||
echo "" >> $GITHUB_STEP_SUMMARY
|
||
echo "**Image:** \`${{ env.DOCKER_IMAGE }}:upstream-${{ steps.date.outputs.date }}\`" >> $GITHUB_STEP_SUMMARY
|
||
echo "" >> $GITHUB_STEP_SUMMARY
|
||
echo "### ✅ Smoke Test 已通過" >> $GITHUB_STEP_SUMMARY
|
||
echo "" >> $GITHUB_STEP_SUMMARY
|
||
echo "| 測試項目 | 狀態 |" >> $GITHUB_STEP_SUMMARY
|
||
echo "|----------|------|" >> $GITHUB_STEP_SUMMARY
|
||
echo "| Container 啟動 | ✅ |" >> $GITHUB_STEP_SUMMARY
|
||
echo "| 工具存在性檢查 | ✅ |" >> $GITHUB_STEP_SUMMARY
|
||
echo "| 最小轉換測試 | ✅ |" >> $GITHUB_STEP_SUMMARY
|
||
echo "| API 端點檢查 | ✅ |" >> $GITHUB_STEP_SUMMARY
|
||
echo "" >> $GITHUB_STEP_SUMMARY
|
||
echo "---" >> $GITHUB_STEP_SUMMARY
|
||
echo "⚠️ 這是 upstream 同步版本。請使用 version tags 取得穩定版本。" >> $GITHUB_STEP_SUMMARY
|
||
echo "" >> $GITHUB_STEP_SUMMARY
|
||
echo "❌ **不會更新 \`latest\` tag**" >> $GITHUB_STEP_SUMMARY
|