From f159ec06e4c1c4a97133d2a6d43e030c13e2f396 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E8=87=A3=E8=B6=85?= <517024110@qq.com> Date: Tue, 29 Sep 2026 13:38:20 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D:=20CI=20Windows=20=E6=9E=84?= =?UTF-8?q?=E5=BB=BA=E5=A4=B1=E8=B4=A5=E2=80=94=E2=80=94=E6=89=93=E5=8C=85?= =?UTF-8?q?=E8=84=9A=E6=9C=AC=E7=BB=9F=E4=B8=80=20UTF-8=20=E8=BE=93?= =?UTF-8?q?=E5=87=BA=EF=BC=8C=E5=88=86=E5=8F=91=E5=8E=8B=E7=BC=A9=E5=8C=85?= =?UTF-8?q?=E6=94=B9=E7=94=B1=E8=84=9A=E6=9C=AC=E5=86=85=E7=BD=AE=EF=BC=88?= =?UTF-8?q?zipfile/tarfile=EF=BC=89=E7=94=9F=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit runner 控制台是 cp1252,构建成功后 print 中文时 UnicodeEncodeError 退出码变 1; 顺手把 tar/PowerShell 打包步骤挪进 Python,zip 条目统一正斜杠,跨平台行为一致 --- .github/workflows/ci.yml | 16 ++++++---------- packaging/build_console.py | 30 ++++++++++++++++++++++++++++++ packaging/build_legacy.py | 5 +++++ 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 327528f..2e0a5d3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,6 +69,8 @@ jobs: - os: ubuntu-latest - os: windows-latest runs-on: ${{ matrix.os }} + env: + PYTHONIOENCODING: utf-8 # Windows runner 控制台 cp1252,防中文输出炸编码 steps: - uses: actions/checkout@v4 @@ -82,21 +84,13 @@ jobs: with: python-version: "3.13" + # build_console.py 构建完直接产出 dist/gpu-fan-console-windows-x64.zip + # 或 dist/gpu-fan-console-linux-x64.tar.gz(zipfile/tarfile 实现,跨平台一致) - name: 构建控制台可执行 run: | pip install -r app/requirements.txt pyinstaller python packaging/build_console.py - - name: 打包分发(Windows → zip / Linux → tar.gz) - shell: bash - run: | - cd dist - if [ "$RUNNER_OS" = "Windows" ]; then - tar -a -cf gpu-fan-console-windows-x64.zip gpu-fan-console - else - tar czf gpu-fan-console-linux-x64.tar.gz gpu-fan-console - fi - - uses: actions/upload-artifact@v4 with: name: gpu-fan-console-${{ matrix.os }} @@ -113,6 +107,8 @@ jobs: - os: ubuntu-latest - os: windows-latest runs-on: ${{ matrix.os }} + env: + PYTHONIOENCODING: utf-8 steps: - uses: actions/checkout@v4 diff --git a/packaging/build_console.py b/packaging/build_console.py index b670152..1334e52 100644 --- a/packaging/build_console.py +++ b/packaging/build_console.py @@ -19,6 +19,35 @@ ROOT = Path(__file__).resolve().parent.parent NAME = "gpu-fan-console" +# Windows 控制台可能是 cp1252/GBK,打印中文会炸 —— 统一按 UTF-8 输出 +for _stream in (sys.stdout, sys.stderr): + if _stream is not None and hasattr(_stream, "reconfigure"): + _stream.reconfigure(encoding="utf-8", errors="replace") + + +def make_archive(exe_dir: Path) -> Path: + """把整目录打成对应平台的分发压缩包(Windows → zip / POSIX → tar.gz)。 + + 放在 Python 里做而不是 workflow 里调 tar/PowerShell:跨平台行为一致, + zip 内条目用正斜杠(Compress-Archive 的反斜杠条目在部分解压器下会显示异常)。 + """ + import tarfile + import zipfile + + suffix = "windows-x64.zip" if os.name == "nt" else "linux-x64.tar.gz" + out = ROOT / "dist" / f"{NAME}-{suffix}" + if out.exists(): + out.unlink() + if suffix.endswith(".zip"): + with zipfile.ZipFile(out, "w", zipfile.ZIP_DEFLATED) as zf: + for path in sorted(exe_dir.rglob("*")): + zf.write(path, path.relative_to(exe_dir.parent).as_posix()) + else: + with tarfile.open(out, "w:gz") as tf: + for path in sorted(exe_dir.rglob("*")): + tf.add(path, arcname=path.relative_to(exe_dir.parent).as_posix()) + return out + def main() -> int: static_dir = ROOT / "app" / "static" @@ -52,6 +81,7 @@ def main() -> int: exe_dir = ROOT / "dist" / NAME shutil.copy2(ROOT / "app" / "config.yaml", exe_dir / "config.yaml") print(f"构建完成: {exe_dir}") + print(f"分发包: {make_archive(exe_dir)}") return 0 diff --git a/packaging/build_legacy.py b/packaging/build_legacy.py index d7f84fa..f8cd4bf 100644 --- a/packaging/build_legacy.py +++ b/packaging/build_legacy.py @@ -15,6 +15,11 @@ ROOT = Path(__file__).resolve().parent.parent ENTRIES = ["fancontroller", "fancontroller_once"] +# Windows 控制台可能是 cp1252/GBK,打印中文会炸 —— 统一按 UTF-8 输出 +for _stream in (sys.stdout, sys.stderr): + if _stream is not None and hasattr(_stream, "reconfigure"): + _stream.reconfigure(encoding="utf-8", errors="replace") + def main() -> int: for name in ENTRIES: