Skip to content

ci: Update release and publish workflow tag templates #52

ci: Update release and publish workflow tag templates

ci: Update release and publish workflow tag templates #52

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:
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@1edb52594c857e2b5b13128931090f0640537287 # v5.3.0
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/[email protected]
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@1edb52594c857e2b5b13128931090f0640537287 # v5.3.0
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: 检出代码
uses: actions/checkout@v4
- name: Setup uv with Python ${{ env.WIN_EXE_PYTHON_VERSION }}
uses: astral-sh/setup-uv@1edb52594c857e2b5b13128931090f0640537287 # v5.3.0
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 "==== 下载并解压 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
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: 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
path: ./build
- name: Create release zip
run: |
zip -9qr "pdf2zh-${{ needs.release-draft.outputs.tag_name }}-win64.zip" ./build/*
- 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