name: fork-build on: workflow_dispatch: # debug purpose # push: env: REGISTRY: ghcr.io REPO_LOWER: ${{ github.repository_owner }}/${{ github.event.repository.name }} GHCR_REPO: ghcr.io/${{ github.repository }} WIN_EXE_PYTHON_VERSION: 3.12.9 jobs: check-repository: name: Check if running in main repository runs-on: ubuntu-latest outputs: is_main_repo: ${{ github.repository == 'Byaidu/PDFMathTranslate' }} steps: - run: echo "Running repository check" test: uses: ./.github/workflows/python-test.yml needs: check-repository if: needs.check-repository.outputs.is_main_repo != 'true' build: strategy: fail-fast: false matrix: include: - platform: linux/amd64 runner: ubuntu-latest - platform: linux/arm64 runner: ubuntu-24.04-arm runs-on: ${{ matrix.runner }} needs: - check-repository - test if: needs.check-repository.outputs.is_main_repo != 'true' permissions: contents: read packages: write steps: - name: Convert to lowercase run: | echo "GHCR_REPO_LOWER=$(echo ${{ env.GHCR_REPO }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV - name: Prepare run: | platform=${{ matrix.platform }} echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV - name: Checkout repository uses: actions/checkout@v4 - name: Docker meta id: meta uses: docker/metadata-action@v5 with: images: | ${{ env.GHCR_REPO_LOWER }} - name: Login to GHCR uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Build and push by digest id: build uses: docker/build-push-action@v6 with: platforms: ${{ matrix.platform }} labels: ${{ steps.meta.outputs.labels }} outputs: type=image,name=${{ env.GHCR_REPO_LOWER }},push-by-digest=true,name-canonical=true,push=true cache-from: ${{ matrix.platform == 'linux/amd64' && 'type=gha' || '' }} cache-to: ${{ matrix.platform == 'linux/amd64' && 'type=gha,mode=max' || '' }} - name: Export digest run: | mkdir -p ${{ runner.temp }}/digests digest="${{ steps.build.outputs.digest }}" touch "${{ runner.temp }}/digests/${digest#sha256:}" - name: Upload digest uses: actions/upload-artifact@v4 with: name: digests-${{ env.PLATFORM_PAIR }} path: ${{ runner.temp }}/digests/* if-no-files-found: error retention-days: 1 merge: runs-on: ubuntu-latest needs: - check-repository - test - build if: needs.check-repository.outputs.is_main_repo != 'true' permissions: contents: read packages: write steps: - name: Convert to lowercase run: | echo "GHCR_REPO_LOWER=$(echo ${{ env.GHCR_REPO }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV - name: Download digests uses: actions/download-artifact@v4 with: path: ${{ runner.temp }}/digests pattern: digests-* merge-multiple: true - name: Login to GHCR uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Docker meta id: meta uses: docker/metadata-action@v5 with: images: | ${{ env.GHCR_REPO_LOWER }} tags: | type=raw,value=dev type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} - name: Create manifest list and push working-directory: ${{ runner.temp }}/digests run: | docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ $(printf '${{ env.GHCR_REPO_LOWER }}@sha256:%s ' *) - name: Inspect image run: | docker buildx imagetools inspect ${{ env.GHCR_REPO_LOWER }}:${{ steps.meta.outputs.version }} build-win64-exe: runs-on: windows-latest needs: - check-repository if: needs.check-repository.outputs.is_main_repo != 'true' steps: - name: 检出代码 uses: actions/checkout@v4 - name: Setup uv with Python ${{ env.WIN_EXE_PYTHON_VERSION }} uses: astral-sh/setup-uv@f94ec6bedd8674c4426838e6b50417d36b6ab231 # v5.3.1 with: python-version: ${{ env.WIN_EXE_PYTHON_VERSION }} enable-cache: true cache-dependency-glob: "pyproject.toml" - name: 执行所有任务(创建目录、下载、解压、复制文件、安装依赖) shell: pwsh run: | Write-Host "==== 创建必要的目录 ====" New-Item -Path "./build" -ItemType Directory -Force New-Item -Path "./build/runtime" -ItemType Directory -Force New-Item -Path "./dep_build" -ItemType Directory -Force Write-Host "==== 复制代码到 dep_build ====" Get-ChildItem -Path "./" -Exclude "dep_build", "build" | Copy-Item -Destination "./dep_build" -Recurse -Force Write-Host "==== 下载并解压 Python ${{ env.WIN_EXE_PYTHON_VERSION }} ====" Write-Host "pythonUrl: https://www.python.org/ftp/python/${{ env.WIN_EXE_PYTHON_VERSION }}/python-${{ env.WIN_EXE_PYTHON_VERSION }}-embed-amd64.zip" $pythonUrl = "https://www.python.org/ftp/python/${{ env.WIN_EXE_PYTHON_VERSION }}/python-${{ env.WIN_EXE_PYTHON_VERSION }}-embed-amd64.zip" $pythonZip = "./dep_build/python.zip" Invoke-WebRequest -Uri $pythonUrl -OutFile $pythonZip Expand-Archive -Path $pythonZip -DestinationPath "./build/runtime" -Force Write-Host "==== 下载 Visual C++ Redistributable 安装程序 ====" $vcRedistUrl = "https://aka.ms/vs/17/release/vc_redist.x64.exe" $vcRedistPath = "./build/无法运行请安装vc_redist.x64.exe" Invoke-WebRequest -Uri $vcRedistUrl -OutFile $vcRedistPath Write-Host "已下载 Visual C++ Redistributable 安装程序到: $vcRedistPath" Write-Host "==== 下载并解压 PyStand ====" $pystandUrl = "https://github.com/skywind3000/PyStand/releases/download/1.1.4/PyStand-v1.1.4-exe.zip" $pystandZip = "./dep_build/PyStand.zip" Invoke-WebRequest -Uri $pystandUrl -OutFile $pystandZip Expand-Archive -Path $pystandZip -DestinationPath "./dep_build/PyStand" -Force Write-Host "==== 复制 PyStand.exe 到 build 并重命名 ====" $pystandExe = "./dep_build/PyStand/PyStand-x64-CLI/PyStand.exe" $destExe = "./build/pdf2zh.exe" if (Test-Path $pystandExe) { Copy-Item -Path $pystandExe -Destination $destExe -Force } else { Write-Host "错误: PyStand.exe 未找到!" exit 1 } Write-Host "==== 创建 Python venv 在 dep_build ====" uv venv ./dep_build/venv ./dep_build/venv/Scripts/activate Write-Host "==== 在 venv 环境中安装项目依赖 ====" uv pip install . Write-Host "==== 复制 venv/Lib/site-packages 到 build/ ====" Copy-Item -Path "./dep_build/venv/Lib/site-packages" -Destination "./build/site-packages" -Recurse -Force Write-Host "==== 复制 script/_pystand_static.int 到 build/ ====" $staticFile = "./script/_pystand_static.int" $destStatic = "./build/_pystand_static.int" if (Test-Path $staticFile) { Copy-Item -Path $staticFile -Destination $destStatic -Force } else { Write-Host "错误: script/_pystand_static.int 未找到!" exit 1 } - name: Upload build artifact uses: actions/upload-artifact@v4 with: name: win64-exe path: ./build if-no-files-found: error compression-level: 1 include-hidden-files: true test-win64-exe: needs: - build-win64-exe - check-repository if: needs.check-repository.outputs.is_main_repo != 'true' runs-on: windows-latest steps: - name: 检出代码 uses: actions/checkout@v4 - name: Download build artifact uses: actions/download-artifact@v4 with: name: win64-exe path: ./build - name: Test show version (online mode) run: | ./build/pdf2zh.exe --version - name: Test - Translate a PDF file with plain text only (online mode) run: | ./build/pdf2zh.exe ./test/file/translate.cli.plain.text.pdf -o ./test/file - name: Test - Translate a PDF file figure run: | ./build/pdf2zh.exe ./test/file/translate.cli.text.with.figure.pdf -o ./test/file - name: Test - Translate without offline assets (online mode) run: | ./build/pdf2zh.exe ./test/file/translate.cli.plain.text.pdf -o ./test/file - name: Upload test results uses: actions/upload-artifact@v4 with: name: test-results path: ./test/file/ if-no-files-found: error - name: Setup uv with Python ${{ env.WIN_EXE_PYTHON_VERSION }} uses: astral-sh/setup-uv@f94ec6bedd8674c4426838e6b50417d36b6ab231 # v5.3.1 with: python-version: ${{ env.WIN_EXE_PYTHON_VERSION }} enable-cache: true cache-dependency-glob: "pyproject.toml" - name: Generate offline assets shell: pwsh run: | Write-Host "==== 生成离线资源包 ====" uv run --active babeldoc --generate-offline-assets ./build - name: Delete cache shell: pwsh run: | Write-Host "==== 删除缓存目录 ====" $cachePath = "$env:USERPROFILE/.cache/babeldoc" if (Test-Path $cachePath) { Remove-Item -Path $cachePath -Recurse -Force Write-Host "已删除缓存目录: $cachePath" } else { Write-Host "缓存目录不存在: $cachePath" } - name: Test - Translate with offline assets (offline mode) run: | Write-Host "==== 测试离线资源包 ====" New-Item -Path "./test/file/offline_result" -ItemType Directory -Force ./build/pdf2zh.exe ./test/file/translate.cli.plain.text.pdf -o ./test/file/offline_result - name: Upload offline test results uses: actions/upload-artifact@v4 with: name: offline-test-results path: ./test/file/offline_result/ if-no-files-found: error - name: Upload build with offline assets artifact uses: actions/upload-artifact@v4 with: name: win64-exe-with-assets path: ./build if-no-files-found: error compression-level: 1 include-hidden-files: true