From 6068a24435291c24504fb577b9eb3131d237109d Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Tue, 20 Oct 2026 12:32:35 +0700 Subject: [PATCH 1/6] ci: run macOS Tests for in-repo pull requests on the Mac --- .claude/rules/ci.md | 3 ++- .github/workflows/macos-tests.yml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.claude/rules/ci.md b/.claude/rules/ci.md index 9f6de3ffb8..287b7054f4 100644 --- a/.claude/rules/ci.md +++ b/.claude/rules/ci.md @@ -43,7 +43,8 @@ paths: ## Runners - Xcode jobs run on `xcode-27`, a GitHub preview image. `setup-xcode` pins the Xcode build. -- Release Build runs on `mac-mini-m4pro` (`self-hosted`, `macOS`, `ARM64`, `tablepro-release`), the maintainer's Mac, as the maintainer's user. `gh variable set MAC_RUNNER --body github` moves it to `xcode-27`; `gh variable delete MAC_RUNNER` moves it back. +- `mac-mini-m4pro` (`self-hosted`, `macOS`, `ARM64`, `tablepro-release`) is the maintainer's Mac. Its runner runs as the standard user `runner`, a LaunchAgent in that user's own login session, so UI tests never touch the maintainer's session or credentials. FileVault blocks auto-login: after a restart, log in to `runner` once and switch back. +- It runs Release Build and macOS Tests for a pull request from a branch in this repository. Fork PRs, pushes, iOS and everything else stay on `xcode-27`. `gh variable set MAC_RUNNER --body github` moves both to `xcode-27`; `gh variable delete MAC_RUNNER` moves them back. - Signing, notarization and the feed stay on hosted runners. - Any workflow on a pushed ref can target that label: never approve a fork PR run that changes `.github/` unread. - At most 5 hosted macOS jobs and 20 jobs in all run at once. diff --git a/.github/workflows/macos-tests.yml b/.github/workflows/macos-tests.yml index 2f6da198de..c1bd03b358 100644 --- a/.github/workflows/macos-tests.yml +++ b/.github/workflows/macos-tests.yml @@ -14,7 +14,8 @@ permissions: jobs: macos: name: macOS Tests - runs-on: xcode-27 + # A pull request from a branch in this repository runs on the Mac, keeping the hosted macOS slots for main and tags. + runs-on: ${{ (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && vars.MAC_RUNNER != 'github') && fromJSON('["self-hosted", "macOS", "ARM64", "tablepro-release"]') || 'xcode-27' }} timeout-minutes: 120 env: NSUnbufferedIO: "YES" From fbfaedcedaf9f52781f5c21b733b32996a7269f8 Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Tue, 20 Oct 2026 13:27:17 +0700 Subject: [PATCH 2/6] ci: skip a diagnostics folder the runner user cannot read --- .github/workflows/macos-tests.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/macos-tests.yml b/.github/workflows/macos-tests.yml index c1bd03b358..90f1eaed2b 100644 --- a/.github/workflows/macos-tests.yml +++ b/.github/workflows/macos-tests.yml @@ -137,7 +137,8 @@ jobs: run: | mkdir -p app-diagnostics for dir in "$HOME/Library/Logs/DiagnosticReports" /Library/Logs/DiagnosticReports; do - [ -d "$dir" ] || continue + # The Mac's runner user is not an admin, so it cannot read the system reports. + [ -d "$dir" ] && [ -r "$dir" ] && [ -x "$dir" ] || continue find "$dir" -maxdepth 2 -iname '*TablePro*' -exec cp {} app-diagnostics/ \; done log show --last 2h --style compact --info --predicate 'process == "TablePro"' \ From 323a5498ea463fa9190c05559f8e91de86cf6798 Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Tue, 20 Oct 2026 13:27:54 +0700 Subject: [PATCH 3/6] docs(ci): name the tools the Mac carries --- .claude/rules/ci.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.claude/rules/ci.md b/.claude/rules/ci.md index 287b7054f4..95dbcbae47 100644 --- a/.claude/rules/ci.md +++ b/.claude/rules/ci.md @@ -43,7 +43,7 @@ paths: ## Runners - Xcode jobs run on `xcode-27`, a GitHub preview image. `setup-xcode` pins the Xcode build. -- `mac-mini-m4pro` (`self-hosted`, `macOS`, `ARM64`, `tablepro-release`) is the maintainer's Mac. Its runner runs as the standard user `runner`, a LaunchAgent in that user's own login session, so UI tests never touch the maintainer's session or credentials. FileVault blocks auto-login: after a restart, log in to `runner` once and switch back. +- `mac-mini-m4pro` (`self-hosted`, `macOS`, `ARM64`, `tablepro-release`) is the maintainer's Mac. Its runner runs as the standard user `runner`, a LaunchAgent in that user's own login session, so UI tests never touch the maintainer's session or credentials. FileVault blocks auto-login: after a restart, log in to `runner` once and switch back. The Mac carries the pinned Xcode and Homebrew `xcbeautify`; jobs bring the rest. - It runs Release Build and macOS Tests for a pull request from a branch in this repository. Fork PRs, pushes, iOS and everything else stay on `xcode-27`. `gh variable set MAC_RUNNER --body github` moves both to `xcode-27`; `gh variable delete MAC_RUNNER` moves them back. - Signing, notarization and the feed stay on hosted runners. - Any workflow on a pushed ref can target that label: never approve a fork PR run that changes `.github/` unread. From e2203b5357a859c9f6fe1cbc1984a0b79e26001f Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Tue, 20 Oct 2026 14:08:03 +0700 Subject: [PATCH 4/6] ci: keep macOS Tests hosted; the Mac runner runs as its own user --- .claude/rules/ci.md | 5 +++-- .github/workflows/macos-tests.yml | 6 ++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.claude/rules/ci.md b/.claude/rules/ci.md index 95dbcbae47..265b1aff75 100644 --- a/.claude/rules/ci.md +++ b/.claude/rules/ci.md @@ -43,8 +43,9 @@ paths: ## Runners - Xcode jobs run on `xcode-27`, a GitHub preview image. `setup-xcode` pins the Xcode build. -- `mac-mini-m4pro` (`self-hosted`, `macOS`, `ARM64`, `tablepro-release`) is the maintainer's Mac. Its runner runs as the standard user `runner`, a LaunchAgent in that user's own login session, so UI tests never touch the maintainer's session or credentials. FileVault blocks auto-login: after a restart, log in to `runner` once and switch back. The Mac carries the pinned Xcode and Homebrew `xcbeautify`; jobs bring the rest. -- It runs Release Build and macOS Tests for a pull request from a branch in this repository. Fork PRs, pushes, iOS and everything else stay on `xcode-27`. `gh variable set MAC_RUNNER --body github` moves both to `xcode-27`; `gh variable delete MAC_RUNNER` moves them back. +- Release Build runs on `mac-mini-m4pro` (`self-hosted`, `macOS`, `ARM64`, `tablepro-release`), the maintainer's Mac. Its runner runs as the standard user `runner`, a LaunchAgent in that user's own login session, with no access to the maintainer's credentials. FileVault blocks auto-login: after a restart, log in to `runner` once and switch back. The Mac carries the pinned Xcode and Homebrew `xcbeautify`; jobs bring the rest. +- `gh variable set MAC_RUNNER --body github` moves Release Build to `xcode-27`; `gh variable delete MAC_RUNNER` moves it back. +- macOS Tests stays hosted: in a session switched to the background, XCUITest hangs before connecting and key-event tests fail (measured on #3694). - Signing, notarization and the feed stay on hosted runners. - Any workflow on a pushed ref can target that label: never approve a fork PR run that changes `.github/` unread. - At most 5 hosted macOS jobs and 20 jobs in all run at once. diff --git a/.github/workflows/macos-tests.yml b/.github/workflows/macos-tests.yml index 90f1eaed2b..2f6da198de 100644 --- a/.github/workflows/macos-tests.yml +++ b/.github/workflows/macos-tests.yml @@ -14,8 +14,7 @@ permissions: jobs: macos: name: macOS Tests - # A pull request from a branch in this repository runs on the Mac, keeping the hosted macOS slots for main and tags. - runs-on: ${{ (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && vars.MAC_RUNNER != 'github') && fromJSON('["self-hosted", "macOS", "ARM64", "tablepro-release"]') || 'xcode-27' }} + runs-on: xcode-27 timeout-minutes: 120 env: NSUnbufferedIO: "YES" @@ -137,8 +136,7 @@ jobs: run: | mkdir -p app-diagnostics for dir in "$HOME/Library/Logs/DiagnosticReports" /Library/Logs/DiagnosticReports; do - # The Mac's runner user is not an admin, so it cannot read the system reports. - [ -d "$dir" ] && [ -r "$dir" ] && [ -x "$dir" ] || continue + [ -d "$dir" ] || continue find "$dir" -maxdepth 2 -iname '*TablePro*' -exec cp {} app-diagnostics/ \; done log show --last 2h --style compact --info --predicate 'process == "TablePro"' \ From 6f96b0e7cb45a3ebbfe80bba2e5459bad4aa6fc9 Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Tue, 20 Oct 2026 14:36:45 +0700 Subject: [PATCH 5/6] ci: drop the self-hosted runner --- .claude/rules/ci.md | 8 ++------ .github/actions/setup-xcodegen/action.yml | 3 +-- .github/workflows/release-build.yml | 15 +++------------ 3 files changed, 6 insertions(+), 20 deletions(-) diff --git a/.claude/rules/ci.md b/.claude/rules/ci.md index 265b1aff75..8c2e00ec5e 100644 --- a/.claude/rules/ci.md +++ b/.claude/rules/ci.md @@ -43,16 +43,12 @@ paths: ## Runners - Xcode jobs run on `xcode-27`, a GitHub preview image. `setup-xcode` pins the Xcode build. -- Release Build runs on `mac-mini-m4pro` (`self-hosted`, `macOS`, `ARM64`, `tablepro-release`), the maintainer's Mac. Its runner runs as the standard user `runner`, a LaunchAgent in that user's own login session, with no access to the maintainer's credentials. FileVault blocks auto-login: after a restart, log in to `runner` once and switch back. The Mac carries the pinned Xcode and Homebrew `xcbeautify`; jobs bring the rest. -- `gh variable set MAC_RUNNER --body github` moves Release Build to `xcode-27`; `gh variable delete MAC_RUNNER` moves it back. -- macOS Tests stays hosted: in a session switched to the background, XCUITest hangs before connecting and key-event tests fail (measured on #3694). -- Signing, notarization and the feed stay on hosted runners. -- Any workflow on a pushed ref can target that label: never approve a fork PR run that changes `.github/` unread. +- Every job runs on a GitHub-hosted runner. There is no self-hosted runner. - At most 5 hosted macOS jobs and 20 jobs in all run at once. ## Release Build -- `release-build.yml` builds and packages the app and every registry plugin as a tag does, unsigned: nightly at 19:00 UTC, Sundays at 07:00 UTC on `xcode-27`, and by dispatch. No tag waits for it. +- `release-build.yml` builds and packages the app and every registry plugin as a tag does, unsigned, on `xcode-27`: nightly at 19:00 UTC and by dispatch. No tag waits for it. ## Summaries diff --git a/.github/actions/setup-xcodegen/action.yml b/.github/actions/setup-xcodegen/action.yml index 2181960469..c827ccb49f 100644 --- a/.github/actions/setup-xcodegen/action.yml +++ b/.github/actions/setup-xcodegen/action.yml @@ -10,8 +10,7 @@ runs: # generated pbxproj output differs between XcodeGen versions. install.sh copies # both bin/xcodegen and share/xcodegen/SettingPresets; without the presets, # generation silently drops build settings and still exits 0. - # It goes to the runner's tool cache, not /usr/local: the self-hosted Mac has no - # passwordless sudo, and the xcodegen already on its PATH is Homebrew's, unverified. + # It goes to the runner's tool cache, not /usr/local, so it needs no sudo. env: XCODEGEN_VERSION: "2.46.0" XCODEGEN_SHA256: "4d9e34b62172d645eed6457cac13fc222569974098ef4ee9c3368bedf0196806" diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml index b92a7f00da..7fd9db9477 100644 --- a/.github/workflows/release-build.yml +++ b/.github/workflows/release-build.yml @@ -1,21 +1,13 @@ name: Release Build # The Release configuration of the app and every registry plugin, universal and unsigned, packaged -# the way a tag packages it, so a packaging failure shows before the tag: nightly on main, weekly on -# the hosted runner, and on demand. +# the way a tag packages it, so a packaging failure shows before the tag: nightly on main and on +# demand. on: schedule: - cron: '0 19 * * *' - # Always hosted, so the MAC_RUNNER fallback stays tested. runs-on matches this string. - - cron: '0 7 * * 0' workflow_dispatch: - inputs: - runner: - description: Where the build runs. default follows the MAC_RUNNER repository variable. - type: choice - options: [default, self-hosted, github] - default: default permissions: contents: read @@ -23,8 +15,7 @@ permissions: jobs: build: name: Release Build - # The dispatch input wins, then the weekly schedule, then MAC_RUNNER; unset is the Mac. - runs-on: ${{ (inputs.runner == 'github' || (inputs.runner != 'self-hosted' && (github.event.schedule == '0 7 * * 0' || vars.MAC_RUNNER == 'github'))) && 'xcode-27' || fromJSON('["self-hosted", "macOS", "ARM64", "tablepro-release"]') }} + runs-on: xcode-27 timeout-minutes: 150 # The tag's version settings run here first; 9999999 is the largest build at MAJOR 0 (apple/RELEASING.md, Versions). env: From cc858c740219b33c6cb488240b12c92a66982914 Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Tue, 20 Oct 2026 14:44:45 +0700 Subject: [PATCH 6/6] ci: drop the Mac from the setup-xcode note --- .github/actions/setup-xcode/action.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/setup-xcode/action.yml b/.github/actions/setup-xcode/action.yml index 55c83eaa60..917c38257a 100644 --- a/.github/actions/setup-xcode/action.yml +++ b/.github/actions/setup-xcode/action.yml @@ -7,8 +7,8 @@ inputs: required: false default: "false" -# The only place the version and build are written. The bundle name differs by machine -# (Xcode_27.1_Release_Candidate.app on xcode-27, Xcode.app on a Mac), so it is found by its version.plist. +# The only place the version and build are written. The image names the bundle by its own scheme +# (Xcode_27.1_Release_Candidate.app on xcode-27), so it is found by its version.plist. runs: using: composite steps: