Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions .github/workflows/build-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Pre-tag macOS build check.
#
# Why this exists: the release pipeline (release.yml) is tag-triggered, so
# until v0.4.0 there was ZERO macOS build coverage before a tag. The G1b
# self-heal code compiled locally (older Swift toolchain, warning only) but
# the macos-14 runner's newer toolchain rejected a weak-`self` capture as a
# hard error — discovered only after tagging. This job runs the SAME build
# step release.yml runs (`make dist-swiftc`) on the SAME runner image, on
# every PR, so compile-time skew surfaces on the PR instead of on the tag.
#
# Build-only: no secrets, no Developer ID signing, no notarization. It proves
# the tree compiles and links; the release pipeline owns sign+notarize.

name: Build check

on:
pull_request:
workflow_dispatch:

jobs:
build:
runs-on: macos-14
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install build dependencies
run: brew install libmtp libusb go

- name: Go bridge — compile + vet (tree-wide vendor consistency)
run: make build-all

- name: Go bridge — tests
run: make bridge-test

- name: macOS app — full build (bridge + swiftc, ad-hoc signed)
# Identical to release.yml's "Build app bundle" step. This is the
# check that would have caught the Swift concurrency error pre-tag.
run: make dist-swiftc
16 changes: 14 additions & 2 deletions MenuBarApp/Sources/DeviceSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,11 @@ final class DeviceSession {

// Surface key bridge-side milestones in the menu while connecting.
bp.onStatusLine = { [weak self] msg in
DispatchQueue.main.async { self?.setConnectStatus(msg) }
// Same weak-var-into-concurrent-closure issue as
// onUnexpectedExit below: DispatchQueue.main.async takes a
// @Sendable closure, so re-capture self immutably in its
// capture list rather than referencing the enclosing weak var.
DispatchQueue.main.async { [weak self] in self?.setConnectStatus(msg) }
}

let useNFS = true
Expand Down Expand Up @@ -216,7 +220,15 @@ final class DeviceSession {
// fault, anything that isn't our own stop()), self-heal instead
// of leaving Finder pointed at a dead mount (G1b).
bp.onUnexpectedExit = { [weak self] in
Task { await self?.handleUnexpectedBridgeExit() }
// Bind a strong, immutable `self` before the Task. A weak
// capture is modeled as a *var* (ARC can zero it across a
// concurrency boundary), so referencing it inside the
// @Sendable Task is "reference to captured var 'self' in
// concurrently-executing code" — a hard error on the macos-14
// runner's Swift toolchain (it only warns locally). guard-let
// makes the capture immutable.
guard let self else { return }
Task { await self.handleUnexpectedBridgeExit() }
}

await MainActor.run {
Expand Down
Loading