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: