From 9d00b408493d61a2b92980d0ff86301591d6fecf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=89=A2=E5=BA=9F=E7=89=A9?= Date: Fri, 30 Oct 2026 20:01:49 +0800 Subject: [PATCH] fix(ci): use Intel macOS runners by default with manual ARM64 builds --- .github/workflows/build.yml | 32 ++++++++++++++++++++++++++++-- .github/workflows/ci.yml | 13 +++++++++++-- docs/BUILDING.md | 39 ++++++++++++++++++++++++++++--------- 3 files changed, 71 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 465f18e..21c6ef9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,6 +14,15 @@ on: - "pyproject.toml" - ".github/workflows/build.yml" workflow_dispatch: + inputs: + macos_arch: + description: "macOS architecture (ARM64 runners may have capacity delays)" + type: choice + required: true + default: x86_64 + options: + - x86_64 + - arm64 permissions: contents: read @@ -36,10 +45,15 @@ jobs: matrix: include: - os: ubuntu-22.04 + arch: x64 artifact: apeiron-linux-x86_64 - - os: macos-14 - artifact: apeiron-macos-arm64 + # Keep routine builds off the capacity-constrained ARM64 pool. + # Native Apple Silicon packages remain available via Run workflow. + - os: ${{ inputs.macos_arch == 'arm64' && 'macos-15' || 'macos-15-intel' }} + arch: ${{ inputs.macos_arch == 'arm64' && 'arm64' || 'x64' }} + artifact: ${{ inputs.macos_arch == 'arm64' && 'apeiron-macos-arm64' || 'apeiron-macos-x86_64' }} - os: windows-2022 + arch: x64 artifact: apeiron-windows-x86_64 runs-on: ${{ matrix.os }} timeout-minutes: 45 @@ -48,7 +62,21 @@ jobs: - uses: actions/setup-python@v5 with: python-version: "3.12" + architecture: ${{ matrix.arch }} cache: pip + - name: Verify interpreter architecture + shell: bash + env: + EXPECTED_ARCH: ${{ matrix.arch }} + run: | + python - <<'PY_ARCH' + import os, platform + expected = os.environ["EXPECTED_ARCH"] + actual = platform.machine().lower() + allowed = {"x64": {"x86_64", "amd64"}, "arm64": {"arm64", "aarch64"}} + assert actual in allowed[expected], f"expected {expected}, got {actual}" + print(f"interpreter architecture: {actual}") + PY_ARCH - name: Install Qt runtime libraries (Linux) if: runner.os == 'Linux' run: | diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2a0bc3b..77191ae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,6 +5,15 @@ on: branches: ["main", "develop"] pull_request: workflow_dispatch: + inputs: + macos_arch: + description: "macOS architecture (ARM64 runners may have capacity delays)" + type: choice + required: true + default: x86_64 + options: + - x86_64 + - arm64 # Least privilege: nothing here writes to the repository. permissions: @@ -53,7 +62,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-24.04, macos-14, windows-2022] + os: [ubuntu-24.04, "${{ inputs.macos_arch == 'arm64' && 'macos-15' || 'macos-15-intel' }}", windows-2022] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 @@ -82,7 +91,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-24.04, macos-14, windows-2022] + os: [ubuntu-24.04, "${{ inputs.macos_arch == 'arm64' && 'macos-15' || 'macos-15-intel' }}", windows-2022] python: ["3.10", "3.12"] runs-on: ${{ matrix.os }} steps: diff --git a/docs/BUILDING.md b/docs/BUILDING.md index c8be5aa..77490e3 100644 --- a/docs/BUILDING.md +++ b/docs/BUILDING.md @@ -52,18 +52,18 @@ The script: 1. rebuilds the native extension in place; 2. compiles translations; 3. runs Nuitka with `--standalone --enable-plugin=pyside6`, including the QML - module, translations and `apeiron._logcore` as data files (the extension is a - compiled C module, not Python source); + module and translations as package data and `apeiron._logcore` as a native + extension module; 4. runs the resulting binary with `--self-check` and fails the build if the report is not `ok`. Artifact layout: ``` -dist/Apeiron/ - apeiron # entry point (apeiron.exe on Windows) +dist/entry.dist/ + ApeironLauncher # entry point (ApeironLauncher.exe on Windows) apeiron/_logcore. # native extension, loaded by the frozen app - apeiron/qml/… # QML module + Material components + apeiron/ui/qml/… # QML module + Material components apeiron/i18n/*.qm # compiled translations PySide6/… # Qt runtime and plugins ``` @@ -72,18 +72,39 @@ Platform notes: * **Windows** — build on Windows with MSVC; the output is a folder that an installer (e.g. Inno Setup, not included here) can package. -* **macOS** — `--macos-create-app-bundle` produces `Apeiron.app`. Signing and - notarisation require an Apple Developer ID; no certificate is available to - this project, so releases are unsigned and Gatekeeper will warn. +* **macOS** — the current build produces a standalone directory, not an `.app` + bundle. Signing and notarisation are not configured. See runner selection below. * **Linux** — the standalone folder is self-contained; ship it as-is, in a tarball, or wrap it with AppImage/deb tooling. ## Verifying a build ```bash -dist/Apeiron/apeiron --self-check | python -c "import json,sys; d=json.load(sys.stdin); assert d['ok']" +dist/entry.dist/ApeironLauncher --self-check | python -c "import json,sys; d=json.load(sys.stdin); assert d['ok']" ``` `--self-check` loads the native extension, opens/migrates SQLite, constructs the QML interface offscreen and reports Qt warnings. A build that cannot do this is not a working build, regardless of whether the compiler succeeded. + + +## macOS hosted runners and architecture + +Routine PR, push and tag workflows use `macos-15-intel` for the macOS jobs. +This is GitHub's standard Intel runner and generates `apeiron-macos-x86_64`. +Intel applications require Rosetta 2 on Apple Silicon; this archive is not a +native ARM64 package. + +GitHub's ARM64 runner pool can fail to acquire a job before any workflow step +starts (`The job was not acquired by Runner ...`, with capacity annotations). +Changing Python dependencies or retrying a step cannot repair that scheduling +failure, and changing `macos-14` to another ARM64 label still uses ARM64 capacity. + +To build or test natively for Apple Silicon, open **Actions → Build** (or **CI**), +choose **Run workflow**, select the branch/ref and set `macos_arch` to `arm64`. +That macOS job uses `macos-15`, and Build uploads `apeiron-macos-arm64`. +The default input is `x86_64`. Build explicitly selects the matching Python +architecture and checks it before compiling, so archive names match the executable. +ARM64 manual jobs may still queue when GitHub has insufficient capacity. + +Runner labels: https://docs.github.com/en/actions/reference/runners/github-hosted-runners