Skip to content
Closed
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
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ jobs:
- name: Checkout
uses: actions/checkout@v4

- name: Select Xcode 26.5 (Swift 6.3.2)
run: sudo xcode-select -s /Applications/Xcode_26.5.app

- name: Show toolchain
run: |
xcode-select -p
Expand Down
49 changes: 39 additions & 10 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ jobs:
- name: Checkout
uses: actions/checkout@v4

- name: Select Xcode 26.5 (Swift 6.3.2)
run: sudo xcode-select -s /Applications/Xcode_26.5.app

- name: Show toolchain
run: |
xcode-select -p
Expand Down Expand Up @@ -68,14 +71,40 @@ jobs:
BUILD_NUMBER: ${{ github.run_number }}
run: ./Scripts/build-app.sh release

- name: Ad-hoc codesign
- name: Import Developer ID certificate
env:
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
MACOS_CERTIFICATE_PWD: ${{ secrets.MACOS_CERTIFICATE_PWD }}
MACOS_KEYCHAIN_PWD: ${{ secrets.MACOS_KEYCHAIN_PWD }}
run: |
# Sign with nothing ("-") so GateKeeper sees a consistent identity.
# Users still see a "can't verify developer" prompt on first launch;
# right-click → Open bypasses it. Proper Developer-ID notarization
# needs a paid Apple account and is deliberately out of scope.
codesign --force --deep --sign - build/Brygga.app
codesign --verify --verbose build/Brygga.app
KEYCHAIN_PATH="$RUNNER_TEMP/build.keychain"
security create-keychain -p "$MACOS_KEYCHAIN_PWD" "$KEYCHAIN_PATH"
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
security unlock-keychain -p "$MACOS_KEYCHAIN_PWD" "$KEYCHAIN_PATH"
echo "$MACOS_CERTIFICATE" | base64 --decode > "$RUNNER_TEMP/cert.p12"
security import "$RUNNER_TEMP/cert.p12" \
-k "$KEYCHAIN_PATH" \
-P "$MACOS_CERTIFICATE_PWD" \
-T /usr/bin/codesign
security list-keychain -d user -s "$KEYCHAIN_PATH"
security set-key-partition-list \
-S apple-tool:,apple: -s -k "$MACOS_KEYCHAIN_PWD" "$KEYCHAIN_PATH"
rm -f "$RUNNER_TEMP/cert.p12"

- name: Developer ID codesign
run: |
IDENTITY=$(security find-identity -v -p codesigning "$RUNNER_TEMP/build.keychain" \
| grep "Developer ID Application" | head -1 | awk '{print $2}')
if [ -z "$IDENTITY" ]; then
echo "::error::No Developer ID Application certificate found in keychain"
exit 1
fi
codesign --force --deep --sign "$IDENTITY" \
--options runtime \
--entitlements Brygga.entitlements \
build/Brygga.app
codesign --verify --verbose --strict build/Brygga.app
echo "Signed with: $IDENTITY"

- name: Create DMG
run: |
Expand Down Expand Up @@ -206,14 +235,14 @@ jobs:
## Installation

1. Download the DMG, open it, drag **Brygga.app** to `/Applications`.
2. First launch: macOS will say the app can't be verified. Right-click (or Ctrl-click) → **Open** → confirm. This is because the binary is ad-hoc signed, not Developer-ID signed.
3. If Gatekeeper still refuses (e.g. *"Apple could not verify Brygga is free of malware"* with no **Open** override), strip the quarantine attribute from the installed bundle and try again:
2. First launch: Gatekeeper may warn that the app is from an identified developer but hasn't been notarized. Right-click (or Ctrl-click) → **Open** → confirm to bypass.
3. If Gatekeeper still refuses, strip the quarantine attribute from the installed bundle and try again:

```sh
xattr -cr /Applications/Brygga.app
```

4. Requires **macOS 15 Sequoia or later, Apple Silicon only**.
4. Requires **macOS 15.4 or later, Apple Silicon only**.

## Release channels

Expand Down
12 changes: 6 additions & 6 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ Brygga is a native macOS IRC client written in pure Swift + SwiftUI. The goal is

## Platform targets

- **Deployment floor:** macOS 15 Sequoia. Official DMGs are Apple Silicon (arm64) only; Intel (x86_64) is source-compatible and users may build their own, but it's untested in CI.
- **Build toolchain:** Xcode 26 / Swift 6.2, strict concurrency on.
- **Policy:** use the newest API that solves the problem; do **not** add `@available` guards. If a new feature needs a newer OS, bump the deployment floor in `Package.swift` + `Scripts/build-app.sh` (`LSMinimumSystemVersion`) in one edit. Today's floor is macOS 15 because `.containerBackground(_:for: .window)` is 15+.
- **Deployment floor:** macOS 15.4 Sequoia. Official DMGs are Apple Silicon (arm64) only; Intel (x86_64) is source-compatible and users may build their own, but it's untested in CI.
- **Build toolchain:** Xcode 26 / Swift 6.3.2, strict concurrency on.
- **Policy:** use the newest API that solves the problem; do **not** add `@available` guards. If a new feature needs a newer OS, bump the deployment floor in `Package.swift` + `Scripts/build-app.sh` (`LSMinimumSystemVersion`) in one edit. Today's floor is macOS 15.4 because `isolated deinit` (SE-0371) requires 15.4+.
- **Design direction:** Liquid Glass on macOS 26; everything in use falls back gracefully on 15 via standard materials.
- No prebuilt Intel binaries. No pre-Sequoia fallbacks.
- No prebuilt Intel binaries. No pre-15.4 fallbacks.

## Layout

Expand All @@ -21,7 +21,7 @@ Brygga is a native macOS IRC client written in pure Swift + SwiftUI. The goal is
- `Tests/` — XCTest. **Only** `@testable import BryggaCore`. Importing the `Brygga` target breaks linkage because of `@main`. There is no UI-test target.
- `Resources/` — icon source art, generated `AppIcon.iconset/`, `AppIcon.icns`. Don't hand-regenerate unless the source art changes.
- `Scripts/build-app.sh` — wraps the SPM-built binary into `build/Brygga.app` with a proper `Info.plist`. A raw `swift run Brygga` does **not** activate as a GUI app — always use the script or the bundle.
- `.github/workflows/ci.yml` — build + test on every push/PR (macOS 26 runner, Xcode 26).
- `.github/workflows/ci.yml` — build + test on every push/PR (macOS 26 runner, Xcode 26.5 beta for Swift 6.3.2).
- `.github/workflows/release.yml` — rolling `nightly` prerelease on every push to `main`; stable release on `v*` tag push.

## Build & test
Expand Down Expand Up @@ -111,7 +111,7 @@ Other on-disk state:
- No force-unwrap outside tests or clearly invariant cases. No `try!` in production code. No `fatalError` except for compile-time-impossible states.
- Doc comments (`///`) on every `public` symbol.
- Default to no comments. Only comment the **why** — a non-obvious constraint, invariant, or workaround. Don't narrate the what.
- Named locals over anonymous closures where the name adds signal. Avoid `prefix` / `suffix` as local-variable names (shadows `Collection.prefix(_:)` / `suffix(_:)` and derails Swift 6.2 overload resolution).
- Named locals over anonymous closures where the name adds signal. Avoid `prefix` / `suffix` as local-variable names (shadows `Collection.prefix(_:)` / `suffix(_:)` and derails Swift 6.3 overload resolution).

## Commits, branches, PRs

Expand Down
10 changes: 4 additions & 6 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
// swift-tools-version: 6.2
// swift-tools-version: 6.3
import PackageDescription

let package = Package(
name: "Brygga",
platforms: [
// macOS 15 Sequoia is the real floor:
// macOS 15.4 is the real floor:
// - `.containerBackground(_:for: .window)` requires macOS 15.
// - `@Observable`, `@Bindable`, `.inspector`, `.onKeyPress`,
// `ContentUnavailableView` all ship from macOS 14, but one newer
// API pulls the whole floor up.
// - `isolated deinit` (SE-0371) requires macOS 15.4.
// Policy: build for latest, raise the floor as needed, no
// `@available` guards.
.macOS(.v15),
.macOS("15.4"),
],
products: [
.executable(name: "Brygga", targets: ["Brygga"]),
Expand Down
2 changes: 1 addition & 1 deletion Scripts/build-app.sh
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ cat > "$APP/Contents/Info.plist" <<PLIST
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>LSMinimumSystemVersion</key>
<string>15.0</string>
<string>15.4</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.social-networking</string>
<key>NSHighResolutionCapable</key>
Expand Down
11 changes: 5 additions & 6 deletions Sources/BryggaCore/Persistence/ScrollbackIndex.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,11 @@ public actor ScrollbackIndex {
}
}

// No `deinit`-time cleanup: actor deinit is nonisolated under strict
// concurrency and can't touch the non-Sendable `OpaquePointer` state.
// `isolated deinit` (SE-0371) would fix this but requires macOS 15.4.
// In practice the production instance is a singleton that never
// deinits, and `:memory:` test instances are reclaimed at process
// exit — SQLite handles that path cleanly.
isolated deinit {
sqlite3_finalize(indexStmt)
sqlite3_finalize(existsStmt)
sqlite3_close_v2(db)
}

// MARK: - Public API

Expand Down
Loading