Skip to content

ci: add check-macos build gate for hot platform-guarded files#3223

Merged
ten9876 merged 1 commit into
mainfrom
auto/ci-add-check-macos-build-gate-for-hot-platform-gua
May 27, 2026
Merged

ci: add check-macos build gate for hot platform-guarded files#3223
ten9876 merged 1 commit into
mainfrom
auto/ci-add-check-macos-build-gate-for-hot-platform-gua

Conversation

@ten9876

@ten9876 ten9876 commented May 27, 2026

Copy link
Copy Markdown
Collaborator

Add a check-macos job to .github/workflows/ci.yml that mirrors
check-windows: same dorny/paths-filter trigger (build-changed),
same hot-file coverage (CMakeLists.txt, third_party/,
.github/workflows/
, src/gui/MainWindow.{cpp,h},
src/core/AudioEngine.{cpp,h}), Apple Silicon runner only,
build-only (no codesign / no macdeployqt / no DMG / no
notarization — those stay in macos-dmg.yml on tag push).

Closes a real coverage gap: today macOS validation only runs on
tag push via macos-dmg.yml, so a Linux-passing change that breaks
macOS compile or link is discovered at release time. AudioEngine
and MainWindow both carry dense Q_OS_MAC / CoreAudio / Cocoa-bridge
code paths that Linux clang doesn't exercise.

Cost is bounded by the same path filter that gates check-windows:
the build group only trips when CMakeLists.txt, third_party/,
workflows, or the two hot files change. macos-15 Apple Silicon
runners are ~10× Linux per-minute, but at the recent build-filter
trip rate (2-3× per week) total additional spend is ~$20/month.

Comment update on the existing filter block: reworded "force the
Windows CI build" → "force the cross-platform CI build" since the
same triggers now protect both check-windows and check-macos.
Same paths, no new entries.

Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com

Add a check-macos job to .github/workflows/ci.yml that mirrors
check-windows: same dorny/paths-filter trigger (build-changed),
same hot-file coverage (CMakeLists.txt, third_party/**,
.github/workflows/**, src/gui/MainWindow.{cpp,h},
src/core/AudioEngine.{cpp,h}), Apple Silicon runner only,
build-only (no codesign / no macdeployqt / no DMG / no
notarization — those stay in macos-dmg.yml on tag push).

Closes a real coverage gap: today macOS validation only runs on
tag push via macos-dmg.yml, so a Linux-passing change that breaks
macOS compile or link is discovered at release time.  AudioEngine
and MainWindow both carry dense Q_OS_MAC / CoreAudio / Cocoa-bridge
code paths that Linux clang doesn't exercise.

Cost is bounded by the same path filter that gates check-windows:
the build group only trips when CMakeLists.txt, third_party/,
workflows, or the two hot files change.  macos-15 Apple Silicon
runners are ~10× Linux per-minute, but at the recent build-filter
trip rate (2-3× per week) total additional spend is ~$20/month.

Comment update on the existing filter block: reworded "force the
Windows CI build" → "force the cross-platform CI build" since the
same triggers now protect both check-windows and check-macos.
Same paths, no new entries.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ten9876 ten9876 requested a review from a team as a code owner May 27, 2026 19:00
@ten9876 ten9876 enabled auto-merge (squash) May 27, 2026 19:00
@ten9876 ten9876 merged commit 9d89873 into main May 27, 2026
4 checks passed
@ten9876 ten9876 deleted the auto/ci-add-check-macos-build-gate-for-hot-platform-gua branch May 27, 2026 19:12
ten9876 added a commit that referenced this pull request May 28, 2026
…per (#3239)

Completes the platform matrix promised in #3232.  Linux (evdev) landed
in #3238; this PR adds the Windows (hidapi) and macOS (IOKit) sides.
The mapper dialog drops its Linux guard and now builds everywhere.

Architecture
------------
All three backends expose the same Qt signal contract:
    tuneSteps(int)               - rotary delta
    buttonEvent(sig, action)     - canonical Linux-keycode-name strings
    connectionChanged(bool, name)

A new typedef `UlanziDialBackend` (src/core/UlanziDialBackend.h)
resolves to the right concrete class per platform.  Mapper dialog and
MainWindow dispatcher use only the alias and have no platform switches
of their own.

Windows (UlanziDialWindowsManager)
----------------------------------
- hidapi (already a project dep) enumerates HID devices, matches by
  product_string substring "Ulanzi Dial"
- Opens every matching interface (the dial enumerates as multiple
  HID interfaces on Windows: Keyboard + Consumer Control, possibly
  Mouse) and polls each via QTimer at 5ms intervals
- HID report parsing: standard 8-byte keyboard report (modifier mask
  + up-to-6-keys array, diffed across frames for transitions) and
  Consumer Control reports (2-byte usage code)
- HID usage codes mapped to canonical Linux keycode integers so the
  signature strings (`KEY_PLAYPAUSE`, `Ctrl+V`, ...) match Linux
  exactly and `kPillSpecs` is unchanged
- Chord assembly state machine identical to the Linux backend
- Hot-plug via 3s rescan timer
- LIMITATION: no exclusive-claim equivalent yet.  The dial's keystrokes
  still reach the focused window because hidapi doesn't suppress OS
  input routing.  RawInput + RIDEV_NOLEGACY is the proper fix; tracked
  for a follow-up

macOS (UlanziDialMacOSManager)
------------------------------
- IOKit HID Manager with device-matching dictionary
  ({ kIOHIDProductKey: "Ulanzi Dial" })
- `IOHIDManagerOpen` with `kIOHIDOptionsTypeSeizeDevice` -- the
  documented macOS exclusive-claim mechanism.  Equivalent to Linux
  EVIOCGRAB: when seized, the dial's events stop reaching the OS
  keyboard stack, so its media keys don't leak to the focused window
- Event-driven via `IOHIDManagerRegisterInputValueCallback`; no polling
- Same usage-page-to-Linux-keycode mapping and chord assembly as
  Windows backend
- Scheduled on `CFRunLoopGetMain()` (IOKit requires a CFRunLoop, and
  only the main thread has one integrated with Qt's event loop on
  macOS).  MainWindow conditionally skips `moveToThread` on macOS to
  match this constraint
- Hot-plug native via IOHIDManager device-matching/removal callbacks

Build wiring (CMakeLists.txt)
-----------------------------
- UlanziDialBackend.h + UlanziDialMapperDialog.{cpp,h}: unconditional
- EvdevEncoderManager.{cpp,h}: UNIX AND NOT APPLE (Linux)
- UlanziDialWindowsManager.{cpp,h}: WIN32
- UlanziDialMacOSManager.{cpp,h}: APPLE
- macOS links -framework IOKit and -framework CoreFoundation

Tested
------
- Linux: compiles + runs as before (Linux behaviour unchanged; same
  EvdevEncoderManager underneath the alias)
- Windows + macOS: compile validation gated by CI (check-windows from
  #3210 and check-macos from #3223).  Live testing on hardware happens
  in the user's environment

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
G6PWY-Chris pushed a commit to G6PWY-Chris/AetherSDR that referenced this pull request Jun 22, 2026
…sdr#3223)

Add a check-macos job to .github/workflows/ci.yml that mirrors
check-windows: same dorny/paths-filter trigger (build-changed),
same hot-file coverage (CMakeLists.txt, third_party/**,
.github/workflows/**, src/gui/MainWindow.{cpp,h},
src/core/AudioEngine.{cpp,h}), Apple Silicon runner only,
build-only (no codesign / no macdeployqt / no DMG / no
notarization — those stay in macos-dmg.yml on tag push).

Closes a real coverage gap: today macOS validation only runs on
tag push via macos-dmg.yml, so a Linux-passing change that breaks
macOS compile or link is discovered at release time.  AudioEngine
and MainWindow both carry dense Q_OS_MAC / CoreAudio / Cocoa-bridge
code paths that Linux clang doesn't exercise.

Cost is bounded by the same path filter that gates check-windows:
the build group only trips when CMakeLists.txt, third_party/,
workflows, or the two hot files change.  macos-15 Apple Silicon
runners are ~10× Linux per-minute, but at the recent build-filter
trip rate (2-3× per week) total additional spend is ~$20/month.

Comment update on the existing filter block: reworded "force the
Windows CI build" → "force the cross-platform CI build" since the
same triggers now protect both check-windows and check-macos.
Same paths, no new entries.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
G6PWY-Chris pushed a commit to G6PWY-Chris/AetherSDR that referenced this pull request Jun 22, 2026
…per (aethersdr#3239)

Completes the platform matrix promised in aethersdr#3232.  Linux (evdev) landed
in aethersdr#3238; this PR adds the Windows (hidapi) and macOS (IOKit) sides.
The mapper dialog drops its Linux guard and now builds everywhere.

Architecture
------------
All three backends expose the same Qt signal contract:
    tuneSteps(int)               - rotary delta
    buttonEvent(sig, action)     - canonical Linux-keycode-name strings
    connectionChanged(bool, name)

A new typedef `UlanziDialBackend` (src/core/UlanziDialBackend.h)
resolves to the right concrete class per platform.  Mapper dialog and
MainWindow dispatcher use only the alias and have no platform switches
of their own.

Windows (UlanziDialWindowsManager)
----------------------------------
- hidapi (already a project dep) enumerates HID devices, matches by
  product_string substring "Ulanzi Dial"
- Opens every matching interface (the dial enumerates as multiple
  HID interfaces on Windows: Keyboard + Consumer Control, possibly
  Mouse) and polls each via QTimer at 5ms intervals
- HID report parsing: standard 8-byte keyboard report (modifier mask
  + up-to-6-keys array, diffed across frames for transitions) and
  Consumer Control reports (2-byte usage code)
- HID usage codes mapped to canonical Linux keycode integers so the
  signature strings (`KEY_PLAYPAUSE`, `Ctrl+V`, ...) match Linux
  exactly and `kPillSpecs` is unchanged
- Chord assembly state machine identical to the Linux backend
- Hot-plug via 3s rescan timer
- LIMITATION: no exclusive-claim equivalent yet.  The dial's keystrokes
  still reach the focused window because hidapi doesn't suppress OS
  input routing.  RawInput + RIDEV_NOLEGACY is the proper fix; tracked
  for a follow-up

macOS (UlanziDialMacOSManager)
------------------------------
- IOKit HID Manager with device-matching dictionary
  ({ kIOHIDProductKey: "Ulanzi Dial" })
- `IOHIDManagerOpen` with `kIOHIDOptionsTypeSeizeDevice` -- the
  documented macOS exclusive-claim mechanism.  Equivalent to Linux
  EVIOCGRAB: when seized, the dial's events stop reaching the OS
  keyboard stack, so its media keys don't leak to the focused window
- Event-driven via `IOHIDManagerRegisterInputValueCallback`; no polling
- Same usage-page-to-Linux-keycode mapping and chord assembly as
  Windows backend
- Scheduled on `CFRunLoopGetMain()` (IOKit requires a CFRunLoop, and
  only the main thread has one integrated with Qt's event loop on
  macOS).  MainWindow conditionally skips `moveToThread` on macOS to
  match this constraint
- Hot-plug native via IOHIDManager device-matching/removal callbacks

Build wiring (CMakeLists.txt)
-----------------------------
- UlanziDialBackend.h + UlanziDialMapperDialog.{cpp,h}: unconditional
- EvdevEncoderManager.{cpp,h}: UNIX AND NOT APPLE (Linux)
- UlanziDialWindowsManager.{cpp,h}: WIN32
- UlanziDialMacOSManager.{cpp,h}: APPLE
- macOS links -framework IOKit and -framework CoreFoundation

Tested
------
- Linux: compiles + runs as before (Linux behaviour unchanged; same
  EvdevEncoderManager underneath the alias)
- Windows + macOS: compile validation gated by CI (check-windows from
  aethersdr#3210 and check-macos from aethersdr#3223).  Live testing on hardware happens
  in the user's environment

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant