Spaces:
Sleeping
Sleeping
| name: Test and Release | |
| on: | |
| push: | |
| branches: | |
| - main | |
| - master | |
| permissions: | |
| id-token: write | |
| contents: write | |
| pull-requests: write | |
| env: | |
| REGISTRY: ghcr.io | |
| REPO_LOWER: ${{ github.repository_owner }}/${{ github.event.repository.name }} | |
| GHCR_REPO: ghcr.io/${{ github.repository }} | |
| DOCKERHUB_REPO: byaidu/pdf2zh | |
| WIN_EXE_PYTHON_VERSION: "3.12.9" | |
| jobs: | |
| check-repository: | |
| name: Check if running in main repository | |
| runs-on: ubuntu-latest | |
| outputs: | |
| # debug purpose | |
| is_main_repo: ${{ github.repository == 'Byaidu/PDFMathTranslate' }} | |
| steps: | |
| - run: echo "Running repository check" | |
| test: | |
| needs: check-repository | |
| uses: ./.github/workflows/python-test.yml | |
| if: needs.check-repository.outputs.is_main_repo == 'true' | |
| build: | |
| name: Build distribution 📦 | |
| needs: [test, check-repository] | |
| if: needs.check-repository.outputs.is_main_repo == 'true' | |
| runs-on: ubuntu-latest | |
| outputs: | |
| is_release: ${{ steps.check-version.outputs.tag }} | |
| version: ${{ steps.check-version.outputs.tag && steps.get-release-version.outputs.version || steps.get-dev-version.outputs.version }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: true | |
| fetch-depth: 2 | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Setup uv with Python 3.12 | |
| uses: astral-sh/setup-uv@f94ec6bedd8674c4426838e6b50417d36b6ab231 # v5.3.1 | |
| with: | |
| python-version: "3.12" | |
| enable-cache: true | |
| cache-dependency-glob: "pyproject.toml" | |
| - name: Check if there is a parent commit | |
| id: check-parent-commit | |
| run: | | |
| echo "sha=$(git rev-parse --verify --quiet HEAD^)" >> $GITHUB_OUTPUT | |
| - name: Detect and tag new version | |
| id: check-version | |
| if: steps.check-parent-commit.outputs.sha | |
| uses: salsify/action-detect-and-tag-new-version@b1778166f13188a9d478e2d1198f993011ba9864 # v2.0.3 | |
| with: | |
| version-command: | | |
| cat pyproject.toml | grep "version = " | head -n 1 | awk -F'"' '{print $2}' | |
| tag-template: 'v{VERSION}' | |
| - name: Install Dependencies | |
| run: | | |
| uv sync | |
| - name: Bump version for developmental release | |
| if: "!steps.check-version.outputs.tag" | |
| id: get-dev-version | |
| run: | | |
| version=$(bumpver update --patch --tag=final --dry 2>&1 | grep "New Version" | awk '{print $NF}') | |
| echo "version=$version.dev$(date +%s)" >> $GITHUB_OUTPUT | |
| bumpver update --set-version $version.dev$(date +%s) | |
| - name: Get release version | |
| if: steps.check-version.outputs.tag | |
| id: get-release-version | |
| run: | | |
| version=$(cat pyproject.toml | grep "version = " | head -n 1 | awk -F'"' '{print $2}') | |
| echo "version=$version" >> $GITHUB_OUTPUT | |
| - name: Build package | |
| run: "uv build" | |
| - name: Store the distribution packages | |
| uses: actions/upload-artifact@v4.6.0 | |
| with: | |
| name: python-package-distributions | |
| path: dist/ | |
| publish-to-pypi: | |
| name: Publish Python 🐍 distribution 📦 to PyPI | |
| if: needs.build.outputs.is_release != '' | |
| needs: | |
| - check-repository | |
| - build | |
| - test-win64-exe | |
| runs-on: ubuntu-latest | |
| environment: | |
| name: pypi | |
| url: https://pypi.org/p/pdf2zh | |
| permissions: | |
| id-token: write | |
| steps: | |
| - name: Download all the dists | |
| uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 | |
| with: | |
| name: python-package-distributions | |
| path: dist/ | |
| - name: Publish distribution 📦 to PyPI | |
| uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4 | |
| publish-to-testpypi: | |
| name: Publish Python 🐍 distribution 📦 to TestPyPI | |
| if: needs.build.outputs.is_release == '' | |
| needs: | |
| - check-repository | |
| - build | |
| - test-win64-exe | |
| runs-on: ubuntu-latest | |
| environment: | |
| name: testpypi | |
| url: https://test.pypi.org/p/pdf2zh | |
| permissions: | |
| id-token: write | |
| steps: | |
| - name: Download all the dists | |
| uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 | |
| with: | |
| name: python-package-distributions | |
| path: dist/ | |
| - name: Publish distribution 📦 to TestPyPI | |
| uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4 | |
| with: | |
| repository-url: https://test.pypi.org/legacy/ | |
| build-docker-image: | |
| 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: | |
| - build | |
| - check-repository | |
| if: needs.check-repository.outputs.is_main_repo == 'true' | |
| environment: | |
| name: ${{ needs.build.outputs.is_release != '' && 'pypi' || 'testpypi' }} | |
| url: ${{ needs.build.outputs.is_release != '' && 'https://hub.docker.com/r/byaidu/pdf2zh/tags?name=latest' || 'https://hub.docker.com/r/byaidu/pdf2zh/tags?name=dev' }} | |
| 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: Setup uv with Python 3.12 | |
| uses: astral-sh/setup-uv@f94ec6bedd8674c4426838e6b50417d36b6ab231 # v5.3.1 | |
| with: | |
| python-version: "3.12" | |
| enable-cache: true | |
| cache-dependency-glob: "pyproject.toml" | |
| - name: Set version from build job | |
| if: needs.build.outputs.is_release == '' | |
| run: | | |
| uv tool install bumpver | |
| echo "Using version: ${{ needs.build.outputs.version }}" | |
| bumpver update --set-version ${{ needs.build.outputs.version }} | |
| - name: Docker meta | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: | | |
| ${{ env.DOCKERHUB_REPO }} | |
| ${{ env.GHCR_REPO_LOWER }} | |
| tags: | | |
| type=raw,value=dev | |
| type=raw,value=${{ needs.build.outputs.version }},enable=${{ needs.build.outputs.is_release != '' }} | |
| type=raw,value=latest,enable=${{ needs.build.outputs.is_release != '' }} | |
| - name: Login to Docker.io | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: docker.io | |
| username: ${{ secrets.DOCKER_USERNAME }} | |
| password: ${{ secrets.DOCKER_PASSWORD }} | |
| - 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.DOCKERHUB_REPO }},${{ 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-docker-image: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| packages: write | |
| needs: | |
| - build-docker-image | |
| - check-repository | |
| - test-win64-exe | |
| - build | |
| if: needs.check-repository.outputs.is_main_repo == 'true' | |
| 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 Docker.io | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: docker.io | |
| username: ${{ secrets.DOCKER_USERNAME }} | |
| password: ${{ secrets.DOCKER_PASSWORD }} | |
| - 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.DOCKERHUB_REPO }} | |
| ${{ env.GHCR_REPO_LOWER }} | |
| tags: | | |
| type=raw,value=dev | |
| type=raw,value=${{ needs.build.outputs.version }},enable=${{ needs.build.outputs.is_release != '' && 'true' || 'false' }} | |
| type=raw,value=latest,enable=${{ needs.build.outputs.is_release != '' && 'true' || 'false' }} | |
| - 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.DOCKERHUB_REPO }}@sha256:%s ' *) | |
| 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.DOCKERHUB_REPO }}:${{ steps.meta.outputs.version }} | |
| 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: checkout babeldoc metadata | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: funstory-ai/BabelDOC | |
| path: babeldoctemp1234567 | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| sparse-checkout: babeldoc/assets/embedding_assets_metadata.py | |
| - name: Cached Assets | |
| id: cache-assets | |
| uses: actions/cache@v4.2.2 | |
| with: | |
| path: ~/.cache/babeldoc | |
| key: test-1-babeldoc-assets-${{ hashFiles('babeldoctemp1234567/babeldoc/assets/embedding_assets_metadata.py') }} | |
| - 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 | |
| - name: Generate offline assets | |
| shell: pwsh | |
| run: | | |
| Write-Host "==== 生成离线资源包 ====" | |
| uv run --active babeldoc --generate-offline-assets ./build | |
| - 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 | |
| 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-with-assets | |
| path: ./build | |
| - name: Test show version | |
| run: | | |
| ./build/pdf2zh.exe --version | |
| - name: Test - Translate a PDF file with plain text only | |
| 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: Delete offline assets and cache | |
| shell: pwsh | |
| run: | | |
| Write-Host "==== 查找并删除离线资源包 ====" | |
| $offlineAssetsPath = Get-ChildItem -Path "./build" -Filter "offline_assets_*.zip" -Recurse | Select-Object -First 1 -ExpandProperty FullName | |
| if ($offlineAssetsPath) { | |
| Write-Host "找到离线资源包: $offlineAssetsPath" | |
| Remove-Item -Path $offlineAssetsPath -Force | |
| Write-Host "已删除离线资源包" | |
| } else { | |
| Write-Host "未找到离线资源包" | |
| } | |
| 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 without offline assets | |
| 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 test results | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: test-results | |
| path: ./test/file/ | |
| release-draft: | |
| name: Release Draft Tasks | |
| needs: | |
| - check-repository | |
| - build | |
| - publish-to-pypi | |
| - publish-to-testpypi | |
| - merge-docker-image | |
| - test-win64-exe | |
| if: | | |
| always() && needs.check-repository.outputs.is_main_repo == 'true' && | |
| (needs.publish-to-pypi.result == 'success' || needs.publish-to-testpypi.result == 'success') && | |
| needs.merge-docker-image.result == 'success' && | |
| needs.test-win64-exe.result == 'success' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| outputs: | |
| tag_name: ${{ steps.release-drafter.outputs.tag_name }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| persist-credentials: true | |
| fetch-depth: 2 | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Publish the release notes | |
| id: release-drafter | |
| uses: release-drafter/release-drafter@b1476f6e6eb133afa41ed8589daba6dc69b4d3f5 # v6.1.0 | |
| with: | |
| publish: ${{ needs.build.outputs.is_release != '' }} | |
| tag: ${{ needs.build.outputs.is_release }} | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| upload-release: | |
| needs: [release-draft, check-repository] | |
| runs-on: ubuntu-latest | |
| if: always() && needs.check-repository.outputs.is_main_repo == 'true' && | |
| needs.release-draft.result == 'success' | |
| steps: | |
| - name: 检出代码 | |
| uses: actions/checkout@v4 | |
| - name: Download build artifact | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: win64-exe-with-assets | |
| path: ./build | |
| - name: Create release zip | |
| run: | | |
| mv ./build ./pdf2zh | |
| zip -9qr "pdf2zh-${{ needs.release-draft.outputs.tag_name }}-with-assets-win64.zip" ./pdf2zh/* | |
| # 查找并删除离线资源文件 | |
| find ./pdf2zh -name "offline_assets_*.zip" -type f -print -delete | |
| # 确保删除操作成功 | |
| echo "Remaining offline assets files (should be empty):" | |
| find ./pdf2zh -name "offline_assets_*.zip" -type f | |
| zip -9qr "pdf2zh-${{ needs.release-draft.outputs.tag_name }}-win64.zip" ./pdf2zh/* | |
| - name: Upload to latest release | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| # Get the latest release (including drafts and pre-releases) | |
| LATEST_RELEASE=${{ needs.release-draft.outputs.tag_name }} | |
| echo "Latest release tag: $LATEST_RELEASE" | |
| # Upload the zip file to the release | |
| gh release upload "$LATEST_RELEASE" "pdf2zh-${{ needs.release-draft.outputs.tag_name }}-win64.zip" --clobber | |
| gh release upload "$LATEST_RELEASE" "pdf2zh-${{ needs.release-draft.outputs.tag_name }}-with-assets-win64.zip" --clobber |