v2.5.2: Publish workflow, README, and pyproject updates
This commit is contained in:
1 parent
5b08aa7ace
commit
aef5acbb45
4 files changed
+247
-240
No files matched your search
+92
-159
@@ -5,41 +5,31 @@ on:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
# =================================================================
|
||||
# ⚙️ 项目参数配置区
|
||||
# =================================================================
|
||||
env:
|
||||
PROJECT_NAME: "qpypack" # 项目包名
|
||||
MAIN_SCRIPT: "qpypack/main.py" # Python 入口文件
|
||||
ICON_PATH: "qpypack/icon.ico" # 默认应用程序图标 (Windows ICO)
|
||||
EXE_NAME_PREFIX: "QPyPack" # 应用程序名前缀
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# 🚀 功能开关
|
||||
# -------------------------------------------------------------
|
||||
BUILD_PIP_PACKAGE: "true" # 是否构建 pip 安装包
|
||||
PUBLISH_TO_PYPI: "true" # 是否发布到 PyPI 官方源
|
||||
# -------------------------------------------------------------
|
||||
|
||||
# 软件元数据 (Windows 平台适用)
|
||||
COMPANY_NAME: "QwejayHuang"
|
||||
PRODUCT_NAME: "QPyPack"
|
||||
LEGAL_COPYRIGHT: "Copyright (C) QwejayHuang"
|
||||
FILE_DESCRIPTION: "Automation Python Packaging and Freezing Suite"
|
||||
# =================================================================
|
||||
PROJECT_NAME: qpypack
|
||||
MAIN_SCRIPT: qpypack/main.py
|
||||
ICON_PATH: qpypack/icon.ico
|
||||
EXE_NAME_PREFIX: QPyPack
|
||||
BUILD_PIP_PACKAGE: true
|
||||
PUBLISH_TO_PYPI: true
|
||||
COMPANY_NAME: QwejayHuang
|
||||
PRODUCT_NAME: QPyPack
|
||||
LEGAL_COPYRIGHT: Copyright (C) QwejayHuang
|
||||
FILE_DESCRIPTION: Automation Python Packaging and Freezing Suite
|
||||
|
||||
jobs:
|
||||
publish-pypi:
|
||||
name: Publish to PyPI and Create Release
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
id-token: write
|
||||
contents: write
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: main
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
@@ -57,34 +47,32 @@ jobs:
|
||||
print(f"Target version to sync: {version}")
|
||||
|
||||
toml_path = 'pyproject.toml'
|
||||
if os.path.exists(toml_path):
|
||||
with open(toml_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
new_content = re.sub(r'(version\s*=\s*["\'])[^"\']+(["\'])', f'\\g<1>{version}\\g<2>', content, count=1)
|
||||
with open(toml_path, 'w', encoding='utf-8') as f:
|
||||
f.write(new_content)
|
||||
print("Successfully synchronized pyproject.toml.")
|
||||
if not os.path.exists(toml_path):
|
||||
raise FileNotFoundError(f"Missing critical configuration: {toml_path}")
|
||||
|
||||
with open(toml_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
new_content = re.sub(r'(version\s*=\s*["\'])[^"\']+(["\'])', f'\\g<1>{version}\\g<2>', content, count=1)
|
||||
with open(toml_path, 'w', encoding='utf-8') as f:
|
||||
f.write(new_content)
|
||||
print("Successfully synchronized pyproject.toml.")
|
||||
|
||||
main_path = os.environ['MAIN_SCRIPT']
|
||||
if os.path.exists(main_path):
|
||||
with open(main_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
new_content = re.sub(r'(__version__\s*=\s*["\'])[^"\']+(["\'])', f'\\g<1>{version}\\g<2>', content, count=1)
|
||||
with open(main_path, 'w', encoding='utf-8') as f:
|
||||
f.write(new_content)
|
||||
print(f"Successfully synchronized {main_path}.")
|
||||
if not os.path.exists(main_path):
|
||||
raise FileNotFoundError(f"Missing critical entrypoint: {main_path}")
|
||||
|
||||
with open(main_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
new_content = re.sub(r'(__version__\s*=\s*["\'])[^"\']+(["\'])', f'\\g<1>{version}\\g<2>', content, count=1)
|
||||
with open(main_path, 'w', encoding='utf-8') as f:
|
||||
f.write(new_content)
|
||||
print(f"Successfully synchronized {main_path}.")
|
||||
|
||||
- name: Commit and push version updates back to repository
|
||||
run: |
|
||||
git config --global user.name "github-actions[bot]"
|
||||
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
if [ -f "pyproject.toml" ]; then
|
||||
git add pyproject.toml "${{ env.MAIN_SCRIPT }}"
|
||||
else
|
||||
git add "${{ env.MAIN_SCRIPT }}"
|
||||
fi
|
||||
|
||||
git add pyproject.toml "${{ env.MAIN_SCRIPT }}"
|
||||
git commit -m "chore: bump version to ${{ github.ref_name }} [skip ci]" || echo "No version changes to commit"
|
||||
git push origin HEAD:main
|
||||
|
||||
@@ -99,50 +87,56 @@ jobs:
|
||||
run: python -m build
|
||||
|
||||
- name: Create GitHub Release
|
||||
if: env.BUILD_PIP_PACKAGE == 'true'
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: |
|
||||
dist/*.whl
|
||||
dist/*.tar.gz
|
||||
fail_on_unmatched_files: false
|
||||
generate_release_notes: true
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Publish to PyPI
|
||||
if: env.PUBLISH_TO_PYPI == 'true' && env.BUILD_PIP_PACKAGE == 'true'
|
||||
if: env.BUILD_PIP_PACKAGE == 'true' && env.PUBLISH_TO_PYPI == 'true'
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
skip-existing: true
|
||||
continue-on-error: true
|
||||
|
||||
build-executables:
|
||||
name: Build Native Executables
|
||||
build-binaries:
|
||||
name: Build Standalone for ${{ matrix.os }} (${{ matrix.arch }})
|
||||
needs: publish-pypi
|
||||
runs-on: ${{ matrix.os }}
|
||||
permissions:
|
||||
contents: write
|
||||
needs: publish-pypi
|
||||
contents: write
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: windows-latest
|
||||
platform: windows
|
||||
archive_ext: .zip
|
||||
- os: macos-latest
|
||||
platform: macos
|
||||
archive_ext: .zip
|
||||
arch: x64
|
||||
artifact_name: QPyPack.exe
|
||||
package_extension: zip
|
||||
- os: macos-14
|
||||
arch: arm64
|
||||
artifact_name: QPyPack.app
|
||||
package_extension: zip
|
||||
- os: ubuntu-latest
|
||||
platform: linux
|
||||
archive_ext: .tar.gz
|
||||
arch: x64
|
||||
artifact_name: QPyPack
|
||||
package_extension: tar.gz
|
||||
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: main
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.10'
|
||||
cache: 'pip'
|
||||
|
||||
- name: Extract version
|
||||
shell: bash
|
||||
@@ -151,31 +145,8 @@ jobs:
|
||||
VERSION=${TAG_NAME#v}
|
||||
echo "VERSION=$VERSION" >> $GITHUB_ENV
|
||||
|
||||
- name: Sync version in Workspace
|
||||
shell: python
|
||||
run: |
|
||||
import re
|
||||
import os
|
||||
|
||||
version = os.environ['VERSION']
|
||||
main_path = os.environ['MAIN_SCRIPT']
|
||||
print(f"Target version to sync on {os.name}: {version}")
|
||||
|
||||
if os.path.exists(main_path):
|
||||
with open(main_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
new_content = re.sub(r'(__version__\s*=\s*["\'])[^"\']+(["\'])', f'\\g<1>{version}\\g<2>', content, count=1)
|
||||
with open(main_path, 'w', encoding='utf-8') as f:
|
||||
f.write(new_content)
|
||||
print(f"Workspace {main_path} synchronized.")
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install PySide6 pyinstaller Pillow
|
||||
|
||||
- name: Generate Windows Version Info File
|
||||
if: matrix.platform == 'windows'
|
||||
if: runner.os == 'Windows'
|
||||
shell: python
|
||||
run: |
|
||||
import os
|
||||
@@ -185,13 +156,6 @@ jobs:
|
||||
parts = re.findall(r'\d+', version)
|
||||
v_num = (parts + ['0', '0', '0', '0'])[:4]
|
||||
v_tuple = ",".join(v_num)
|
||||
|
||||
company = os.environ.get('COMPANY_NAME', 'My Company')
|
||||
product = os.environ.get('PRODUCT_NAME', 'My Product')
|
||||
copyright_str = os.environ.get('LEGAL_COPYRIGHT', 'Copyright')
|
||||
description = os.environ.get('FILE_DESCRIPTION', 'Python Executable')
|
||||
exe_name = os.environ.get('EXE_NAME_PREFIX', 'App')
|
||||
|
||||
print(f"Windows FileVersion tuple parsed: ({v_tuple})")
|
||||
|
||||
info_content = f"""
|
||||
@@ -212,13 +176,13 @@ jobs:
|
||||
StringTable(
|
||||
'040904B0',
|
||||
[
|
||||
StringStruct('CompanyName', '{company}'),
|
||||
StringStruct('FileDescription', '{description}'),
|
||||
StringStruct('CompanyName', '${{ env.COMPANY_NAME }}'),
|
||||
StringStruct('FileDescription', '${{ env.FILE_DESCRIPTION }}'),
|
||||
StringStruct('FileVersion', '{version}'),
|
||||
StringStruct('InternalName', '{exe_name}'),
|
||||
StringStruct('LegalCopyright', '{copyright_str}'),
|
||||
StringStruct('OriginalFilename', '{exe_name}.exe'),
|
||||
StringStruct('ProductName', '{product}'),
|
||||
StringStruct('InternalName', '${{ env.EXE_NAME_PREFIX }}'),
|
||||
StringStruct('LegalCopyright', '${{ env.LEGAL_COPYRIGHT }}'),
|
||||
StringStruct('OriginalFilename', '${{ env.EXE_NAME_PREFIX }}.exe'),
|
||||
StringStruct('ProductName', '${{ env.PRODUCT_NAME }}'),
|
||||
StringStruct('ProductVersion', '{version}')
|
||||
]
|
||||
)
|
||||
@@ -231,81 +195,50 @@ jobs:
|
||||
with open('file_version_info.txt', 'w', encoding='utf-8') as f:
|
||||
f.write(info_content.strip())
|
||||
|
||||
- name: Configure Icon Options
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install PySide6 Pillow pyinstaller
|
||||
|
||||
- name: Build with PyInstaller (Windows with Version Info)
|
||||
if: runner.os == 'Windows'
|
||||
run: |
|
||||
pyinstaller --clean --onefile --noconsole --version-file=file_version_info.txt --icon=${{ env.ICON_PATH }} --add-data "${{ env.ICON_PATH }};." --name="${{ env.EXE_NAME_PREFIX }}_${{ env.VERSION }}" ${{ env.MAIN_SCRIPT }}
|
||||
|
||||
- name: Build with PyInstaller (macOS / Linux)
|
||||
if: runner.os != 'Windows'
|
||||
shell: bash
|
||||
run: |
|
||||
ICON_OPTION=""
|
||||
if [ "${{ matrix.platform }}" = "windows" ]; then
|
||||
if [ -f "${{ env.ICON_PATH }}" ]; then
|
||||
ICON_OPTION="--icon=\"${{ env.ICON_PATH }}\" --add-data=\"${{ env.ICON_PATH }};.\""
|
||||
fi
|
||||
elif [ "${{ matrix.platform }}" = "macos" ]; then
|
||||
# macOS 优先检查工程是否存在标准的 .icns/icon.png,否则采用默认图标降级匹配
|
||||
if [ -f "qpypack/icon.icns" ]; then
|
||||
ICON_OPTION="--icon=\"qpypack/icon.icns\" --add-data=\"qpypack/icon.icns:.\""
|
||||
elif [ -f "qpypack/icon.png" ]; then
|
||||
ICON_OPTION="--icon=\"qpypack/icon.png\" --add-data=\"qpypack/icon.png:.\""
|
||||
elif [ -f "${{ env.ICON_PATH }}" ]; then
|
||||
ICON_OPTION="--icon=\"${{ env.ICON_PATH }}\" --add-data=\"${{ env.ICON_PATH }}:.\""
|
||||
fi
|
||||
elif [ "${{ matrix.platform }}" = "linux" ]; then
|
||||
if [ -f "qpypack/icon.png" ]; then
|
||||
ICON_OPTION="--add-data=\"qpypack/icon.png:.\""
|
||||
elif [ -f "${{ env.ICON_PATH }}" ]; then
|
||||
ICON_OPTION="--add-data=\"${{ env.ICON_PATH }}:.\""
|
||||
fi
|
||||
if [ "${{ runner.os }}" = "macOS" ]; then
|
||||
pyinstaller --onefile --noconsole --name=${{ env.EXE_NAME_PREFIX }} --icon=${{ env.ICON_PATH }} ${{ env.MAIN_SCRIPT }}
|
||||
else
|
||||
pyinstaller --onefile --noconsole --name=${{ env.EXE_NAME_PREFIX }} ${{ env.MAIN_SCRIPT }}
|
||||
fi
|
||||
echo "ICON_OPTION=$ICON_OPTION" >> $GITHUB_ENV
|
||||
|
||||
- name: Build EXE / APP / Binary with PyInstaller
|
||||
shell: bash
|
||||
run: |
|
||||
PYINSTALLER_CMD="pyinstaller --clean --onefile --noconsole"
|
||||
|
||||
# Windows 元数据配置
|
||||
if [ "${{ matrix.platform }}" = "windows" ] && [ -f "file_version_info.txt" ]; then
|
||||
PYINSTALLER_CMD="$PYINSTALLER_CMD --version-file=file_version_info.txt"
|
||||
fi
|
||||
|
||||
# macOS Bundle 属性标识符配置
|
||||
if [ "${{ matrix.platform }}" = "macos" ]; then
|
||||
PYINSTALLER_CMD="$PYINSTALLER_CMD --osx-bundle-identifier=\"com.qwejayhuang.qpypack\""
|
||||
fi
|
||||
|
||||
# 挂载图标参数
|
||||
if [ -n "$ICON_OPTION" ]; then
|
||||
PYINSTALLER_CMD="$PYINSTALLER_CMD $ICON_OPTION"
|
||||
fi
|
||||
|
||||
TARGET_NAME="${{ env.EXE_NAME_PREFIX }}_${{ env.VERSION }}"
|
||||
PYINSTALLER_CMD="$PYINSTALLER_CMD --name=\"$TARGET_NAME\" ${{ env.MAIN_SCRIPT }}"
|
||||
|
||||
echo "Executing build: $PYINSTALLER_CMD"
|
||||
eval $PYINSTALLER_CMD
|
||||
|
||||
- name: Compress Build Artifact (Windows)
|
||||
if: matrix.platform == 'windows'
|
||||
- name: Package Artifact (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
shell: pwsh
|
||||
run: |
|
||||
Compress-Archive -Path "dist/${{ env.EXE_NAME_PREFIX }}_${{ env.VERSION }}.exe" -DestinationPath "dist/${{ env.EXE_NAME_PREFIX }}_${{ env.VERSION }}.zip"
|
||||
$OUT_NAME = "${{ env.EXE_NAME_PREFIX }}-${{ env.VERSION }}-windows-${{ matrix.arch }}"
|
||||
Compress-Archive -Path "dist/${{ env.EXE_NAME_PREFIX }}_${{ env.VERSION }}.exe" -DestinationPath "$OUT_NAME.zip" -Force
|
||||
|
||||
- name: Compress Build Artifact (macOS)
|
||||
if: matrix.platform == 'macos'
|
||||
- name: Package Artifact (macOS / Linux)
|
||||
if: runner.os != 'Windows'
|
||||
shell: bash
|
||||
run: |
|
||||
VERSION=${{ env.VERSION }}
|
||||
OS_NAME=$(echo "${{ runner.os }}" | tr '[:upper:]' '[:lower:]')
|
||||
OUT_NAME="${{ env.EXE_NAME_PREFIX }}-${VERSION}-${OS_NAME}-${{ matrix.arch }}"
|
||||
cd dist
|
||||
zip -r "${{ env.EXE_NAME_PREFIX }}_${{ env.VERSION }}.zip" "${{ env.EXE_NAME_PREFIX }}_${{ env.VERSION }}.app"
|
||||
if [ "${{ matrix.package_extension }}" = "zip" ]; then
|
||||
zip -r "../${OUT_NAME}.zip" "${{ matrix.artifact_name }}"
|
||||
else
|
||||
tar -czf "../${OUT_NAME}.tar.gz" "${{ matrix.artifact_name }}"
|
||||
fi
|
||||
|
||||
- name: Compress Build Artifact (Linux)
|
||||
if: matrix.platform == 'linux'
|
||||
shell: bash
|
||||
run: |
|
||||
cd dist
|
||||
tar -czvf "${{ env.EXE_NAME_PREFIX }}_${{ env.VERSION }}.tar.gz" "${{ env.EXE_NAME_PREFIX }}_${{ env.VERSION }}"
|
||||
|
||||
- name: Upload Artifacts to GitHub Release
|
||||
- name: Append Standalone Binary to GitHub Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: dist/${{ env.EXE_NAME_PREFIX }}_${{ env.VERSION }}${{ matrix.archive_ext }}
|
||||
files: ${{ env.EXE_NAME_PREFIX }}-*.*
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
Reference in new issue
Block a user