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
43 changes: 34 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,42 @@ concurrency:

jobs:
swift:
name: Swift build + tests (macOS)
runs-on: macos-15
name: Swift build + tests (${{ matrix.name }})
runs-on: ${{ matrix.runner }}
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
include:
- name: macOS 15, Xcode 26.2
slug: macos15
runner: macos-15
xcode: "26.2"
# The OS most users are on: build with its SDK and run the tests on
# it. GitHub names this image after its Xcode; it runs macOS 27.
- name: macOS 27, Xcode 27
slug: macos27
runner: xcode-27
xcode: "27"
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Select Xcode
- name: Select Xcode ${{ matrix.xcode }}
run: |
sudo xcode-select -s /Applications/Xcode_26.2.app 2>/dev/null \
|| sudo xcode-select -s "$(ls -d /Applications/Xcode_2*.app | sort -V | tail -1)"
sw_vers
# The newest release of this Xcode, not a beta. A version-named path
# can be a link to a beta (Xcode_27.2.app -> Xcode_27.2_beta.app),
# so judge each by where it really points.
XCODE="$(for app in /Applications/Xcode_${{ matrix.xcode }}*.app; do realpath "$app"; done \
| grep -viE 'beta|release_candidate|_rc' | sort -uV | tail -1)"
if [ -z "$XCODE" ]; then
echo "::error::no release of Xcode ${{ matrix.xcode }} on this runner"
ls -l /Applications | grep Xcode
exit 1
fi
echo "Using $XCODE"
sudo xcode-select -s "$XCODE"
xcodebuild -version

- name: Install XcodeGen
Expand All @@ -37,8 +62,8 @@ jobs:
uses: actions/cache@v4
with:
path: build/SourcePackages
key: spm-${{ runner.os }}-${{ hashFiles('project.yml') }}
restore-keys: spm-${{ runner.os }}-
key: spm-${{ matrix.slug }}-${{ hashFiles('project.yml') }}
restore-keys: spm-${{ matrix.slug }}-

- name: Resolve Swift package dependencies
run: |
Expand Down Expand Up @@ -122,7 +147,7 @@ jobs:
if: always()
uses: actions/upload-artifact@v4
with:
name: xcodebuild-logs
name: xcodebuild-logs-${{ matrix.slug }}
path: |
build/xcodebuild-build.log
build/xcodebuild-test.log
Expand All @@ -133,7 +158,7 @@ jobs:
if: failure()
uses: actions/upload-artifact@v4
with:
name: test-results
name: test-results-${{ matrix.slug }}
path: build/TestResults.xcresult
if-no-files-found: ignore

Expand Down
31 changes: 26 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,23 +43,44 @@ jobs:
# One DMG per architecture rather than a universal app: each download is
# half the size, and the Intel build says plainly that it cannot run the
# local MLX engines.
#
# Apple silicon is built with Xcode 27 on macOS 27, so the app is linked
# against the macOS 27 SDK and gets that system's behavior rather than a
# compatibility mode. macOS 27 does not run on Intel Macs, so the Intel
# build stays on the toolchain of the last system they run.
build:
name: Build DMG (${{ matrix.arch }})
needs: [version, test]
runs-on: macos-15
runs-on: ${{ matrix.runner }}
strategy:
matrix:
arch: [arm64, x86_64]
include:
- arch: arm64
runner: xcode-27
xcode: "27"
- arch: x86_64
runner: macos-15
xcode: "26.2"
env:
VERSION: ${{ needs.version.outputs.version }}
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Select Xcode
- name: Select Xcode ${{ matrix.xcode }}
run: |
sudo xcode-select -s /Applications/Xcode_26.2.app 2>/dev/null \
|| sudo xcode-select -s "$(ls -d /Applications/Xcode_2*.app | sort -V | tail -1)"
# The newest release of this Xcode, not a beta. A version-named path
# can be a link to a beta (Xcode_27.2.app -> Xcode_27.2_beta.app),
# so judge each by where it really points.
XCODE="$(for app in /Applications/Xcode_${{ matrix.xcode }}*.app; do realpath "$app"; done \
| grep -viE 'beta|release_candidate|_rc' | sort -uV | tail -1)"
if [ -z "$XCODE" ]; then
echo "::error::no release of Xcode ${{ matrix.xcode }} on this runner"
ls -l /Applications | grep Xcode
exit 1
fi
echo "Using $XCODE"
sudo xcode-select -s "$XCODE"
xcodebuild -version

- name: Install XcodeGen
Expand Down
4 changes: 2 additions & 2 deletions Core/Pipeline/TranscriptBuffer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ final class TranscriptBuffer: ObservableObject {
/// the only signal, so a line whose translation failed, came back empty, was
/// covered by a later T3PO translation or was never sent (translation off)
/// showed as translating forever.
enum LiveTranslationState: Hashable {
enum LiveTranslationState: Hashable, Sendable {
/// Not sent: translation was off, or the line's sentence has not ended.
case notSent
case pending
Expand All @@ -105,7 +105,7 @@ enum LiveTranslationState: Hashable {
case merged
}

struct LiveSegment: Identifiable, Hashable {
struct LiveSegment: Identifiable, Hashable, Sendable {
let id: Int64 // = rowId
let rowId: Int64
let startMs: Int64
Expand Down
3 changes: 3 additions & 0 deletions Features/Shared/SessionPresentation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ struct SentenceBlock<Line: Identifiable>: Identifiable {
var id: Line.ID { lines[0].id }
}

/// So the rows' nonisolated `==` can read a block off a main-actor view.
extension SentenceBlock: Sendable where Line: Sendable {}

extension Array where Element == Segment {
var sentenceBlocks: [SentenceBlock<Segment>] {
SentenceGroups.group(self).map { SentenceBlock(lines: $0) }
Expand Down
4 changes: 2 additions & 2 deletions project.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ settings:
SWIFT_STRICT_CONCURRENCY: complete
SWIFT_TREAT_WARNINGS_AS_ERRORS: "NO"
ENABLE_USER_SCRIPT_SANDBOXING: NO
MARKETING_VERSION: "0.16.6"
CURRENT_PROJECT_VERSION: "24"
MARKETING_VERSION: "0.16.7"
CURRENT_PROJECT_VERSION: "25"
DEAD_CODE_STRIPPING: YES

packages:
Expand Down
Loading