Skip to content

Commit 5c7bab4

Browse files
committed
Add Intel macOS 13.5 support
1 parent 3dee523 commit 5c7bab4

13 files changed

Lines changed: 1777 additions & 152 deletions

.github/workflows/ci.yml

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@ name: CI
22

33
on:
44
push:
5-
branches: [main]
5+
branches: [main, macos13-intel]
66
pull_request:
77

8+
permissions:
9+
contents: read
10+
811
jobs:
912
app:
1013
name: macOS app
@@ -33,6 +36,44 @@ jobs:
3336
zsh -n scripts/notarize-pkg.sh
3437
zsh -n scripts/uninstall.sh
3538
39+
intel-compatibility:
40+
name: Intel x86_64 compatibility
41+
runs-on: macos-15-intel
42+
steps:
43+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
44+
- name: Build and test x86_64
45+
run: |
46+
test "$(uname -m)" = "x86_64"
47+
sw_vers
48+
swift --version
49+
xcodebuild -version
50+
swift build -c release
51+
swift test
52+
- name: Verify deployment targets
53+
run: |
54+
file .build/release/Capsomnia | grep -q 'x86_64'
55+
file .build/release/capsomnia-pmset | grep -q 'x86_64'
56+
otool -l .build/release/Capsomnia | grep -A3 LC_BUILD_VERSION | grep -q 'minos 13.5'
57+
otool -l .build/release/capsomnia-pmset | grep -A3 LC_BUILD_VERSION | grep -q 'minos 13.5'
58+
./scripts/build-app.sh
59+
test "$(/usr/libexec/PlistBuddy -c 'Print :LSMinimumSystemVersion' dist/Capsomnia.app/Contents/Info.plist)" = "13.5"
60+
61+
swift-59-compatibility:
62+
name: Swift 5.9 language compatibility
63+
runs-on: macos-14
64+
steps:
65+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
66+
- name: Build and test with Xcode 15.2
67+
run: |
68+
sudo xcode-select -s /Applications/Xcode_15.2.app
69+
test "$(uname -m)" = "x86_64"
70+
swift --version | tee "$RUNNER_TEMP/swift-version"
71+
grep -Eq 'Swift version 5\.9(\.| )' "$RUNNER_TEMP/swift-version"
72+
xcodebuild -version | tee "$RUNNER_TEMP/xcode-version"
73+
grep -q 'Xcode 15.2' "$RUNNER_TEMP/xcode-version"
74+
swift build -c release
75+
swift test
76+
3677
site:
3778
name: GitHub Pages site
3879
runs-on: ubuntu-latest

INTEL-MACOS13-VALIDATION.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Intel macOS 13.5 Validation
2+
3+
This fork supports source installation on Intel Macs running macOS 13.5 or later with Xcode 15 and Swift 5.9.
4+
5+
## Automated checks
6+
7+
Run from the repository root:
8+
9+
```sh
10+
swift --version
11+
xcodebuild -version
12+
swift build -c release
13+
swift test
14+
./scripts/test-install-lifecycle.sh
15+
SKIP_SIGNING=true ./scripts/build-pkg.sh "$TMPDIR/capsomnia-pkg"
16+
```
17+
18+
Both release executables must be x86_64 and declare a 13.5 deployment target:
19+
20+
```sh
21+
file .build/release/Capsomnia .build/release/capsomnia-pmset
22+
otool -l .build/release/Capsomnia | grep -A3 LC_BUILD_VERSION
23+
otool -l .build/release/capsomnia-pmset | grep -A3 LC_BUILD_VERSION
24+
```
25+
26+
## Source-install ownership
27+
28+
The source installer owns only `~/Applications/Capsomnia.app`, the user LaunchAgent, and shared privileged files proven by its root-owned receipt. It refuses to overwrite package-owned, foreign, symlinked, or otherwise unproven state. The package installation under `/Applications` and its system LaunchAgent remain package-owned.
29+
30+
Installation uses a root-owned transaction journal. A failure before commit restores the recorded pre-state and normal sleep. If rollback or sleep-state verification cannot be proven, the command exits nonzero and retains the journal as recovery evidence.
31+
Install and uninstall operations share a root-owned lifecycle lock. Uninstall snapshots every managed asset and restores the complete pre-state if any deletion fails, leaving recovery evidence when rollback cannot be verified.
32+
33+
Package installs write `/Library/Application Support/Capsomnia/package-install.receipt`, which binds the package app, system LaunchAgent, helper, and sudoers rule. Package preinstall refuses a source receipt and refuses any existing managed target unless every target still matches that package receipt. Before payload replacement, preinstall snapshots every package-owned asset. If activation fails, postinstall restores the complete prior payload, sudoers rule, receipt, and console-user runtime state; unverifiable rollback retains the snapshot as recovery evidence. The new receipt is committed only after LaunchAgent verification. Neither package install nor uninstall removes unreceipted legacy paths. The bundled uninstaller applies the same collision, symlink, foreign-file, and provenance-drift checks.
34+
35+
## Manual Intel hardware checklist
36+
37+
1. Confirm `uname -m` is `x86_64` and macOS is 13.5 or later.
38+
2. Run `./scripts/install.sh` and supply administrator credentials.
39+
3. Confirm the app starts from `~/Applications/Capsomnia.app` and its LaunchAgent is loaded and running.
40+
4. Turn Caps Lock on and verify the menu-bar active indicator and `pmset -g` `SleepDisabled 1` agree.
41+
5. Turn Caps Lock off and verify `SleepDisabled 0`.
42+
6. Run `swift test` to cover shortcut handling, all-caps prevention, Accessibility failure behavior, and settings persistence regressions.
43+
7. With safe power and ventilation, verify closed-lid behavior with and without an external display.
44+
8. Quit or uninstall the app and verify normal sleep is restored.
45+
9. Run the lifecycle harness to verify same-owner reinstall/update, settings preservation, and ownership validation.
46+
10. Run the bundled source uninstaller and verify `SleepDisabled 0`; confirm package-owned paths are untouched.
47+
48+
Do not mark a release compatible until every automated check and applicable hardware item passes on an Intel Mac.
49+
## Recovery evidence
50+
51+
Source installations use receipt format v3 at `/Library/Application Support/Capsomnia/source-install.receipt`. The receipt binds each managed path, its ownership and mode, the full app-tree hash, and the transaction identifier. Transaction journals are stored under `/Library/Application Support/Capsomnia/transactions/<transaction-id>`.
52+
53+
A successful rollback removes its journal. An unsafe terminal state leaves the journal in place with a `rollback-unverified` marker and exits nonzero. Do not delete that evidence until the machine state has been inspected and restored. To force normal sleep during manual recovery, run:
54+
55+
```sh
56+
sudo pmset -a disablesleep 0
57+
pmset -g | grep SleepDisabled
58+
```
59+
60+
Confirm the second command reports `SleepDisabled 0`, inspect the retained snapshots and state markers, and restore the affected paths from the journal before removing it. Unsafe uninstall rollback prints its recovery ID, retained journal path, and deterministic recovery procedure. The source uninstaller removes shared assets only when the v3 receipt still proves ownership; provenance drift requires manual inspection rather than destructive cleanup.
61+
62+
Source builds are unsigned. macOS may require an explicit local trust decision, and this fork does not claim upstream signing or notarization.

Package.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
// swift-tools-version: 6.0
1+
// swift-tools-version: 5.9
22

33
import PackageDescription
44

55
let package = Package(
66
name: "Capsomnia",
77
platforms: [
8-
.macOS(.v14)
8+
.macOS("13.5")
99
],
1010
products: [
1111
.executable(name: "Capsomnia", targets: ["Capsomnia"]),
@@ -23,5 +23,5 @@ let package = Package(
2323
dependencies: ["Capsomnia"]
2424
)
2525
],
26-
swiftLanguageModes: [.v5]
26+
swiftLanguageVersions: [.v5]
2727
)

README.ja.md

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515

1616
<p align="center">
1717
<a href="https://github.com/fuji-mak/Capsomnia/actions/workflows/ci.yml"><img alt="CI" src="https://img.shields.io/github/actions/workflow/status/fuji-mak/Capsomnia/ci.yml?branch=main&style=flat-square&label=CI&labelColor=111111&color=b7ff3c"></a>
18-
<img alt="macOS 14+" src="https://img.shields.io/badge/macOS-14%2B-b7ff3c?style=flat-square&labelColor=111111">
19-
<img alt="Swift 6" src="https://img.shields.io/badge/Swift-6-b7ff3c?style=flat-square&labelColor=111111">
18+
<img alt="macOS 13.5+" src="https://img.shields.io/badge/macOS-13.5%2B-b7ff3c?style=flat-square&labelColor=111111">
19+
<img alt="Swift 5.9" src="https://img.shields.io/badge/Swift-5.9-b7ff3c?style=flat-square&labelColor=111111">
2020
<a href="LICENSE"><img alt="MIT License" src="https://img.shields.io/badge/License-MIT-b7ff3c?style=flat-square&labelColor=111111"></a>
2121
</p>
2222

@@ -44,7 +44,8 @@ Capsomnia 本体はネットワーク通信を行わず、テレメトリを収
4444

4545
必要なもの:
4646

47-
- Apple silicon Mac(macOS 14以降)
47+
- 署名済み upstream パッケージ: macOS 14 以降の Apple silicon Mac
48+
- この fork のソースインストール: macOS 13.5 以降の Intel Mac
4849
- インストール時の管理者権限
4950

5051
署名済みパッケージでインストール:
@@ -58,16 +59,18 @@ Capsomnia 本体はネットワーク通信を行わず、テレメトリを収
5859

5960
## ソースからビルド
6061

61-
開発者向けのソースインストールも利用できます。こちらは Swift 6 toolchain が必要です。
62+
ソースインストールには Xcode 15 に含まれる Swift 5.9 toolchain が必要です。
6263

6364
```sh
64-
git clone https://github.com/fuji-mak/Capsomnia.git
65+
git clone --branch macos13-intel https://github.com/hubeen/Capsomnia.git
6566
cd Capsomnia
6667
./scripts/install.sh
6768
```
6869

6970
ソースインストーラはローカルで `Capsomnia.app` をビルドし、`~/Applications/` に配置します。あわせて、同じ helper、sudoers rule、ユーザー LaunchAgent を設定します。
7071

72+
所有権ルールと必須の Intel ハードウェアチェックリストは [`INTEL-MACOS13-VALIDATION.md`](INTEL-MACOS13-VALIDATION.md) を参照してください。
73+
7174
## できること
7275

7376
- 大文字固定を防ぐ(任意): Capsomniaがオンのときに、入力が大文字になるのを無効化します。Shiftでの大文字入力は維持します。

README.ko.md

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515

1616
<p align="center">
1717
<a href="https://github.com/fuji-mak/Capsomnia/actions/workflows/ci.yml"><img alt="CI" src="https://img.shields.io/github/actions/workflow/status/fuji-mak/Capsomnia/ci.yml?branch=main&style=flat-square&label=CI&labelColor=111111&color=b7ff3c"></a>
18-
<img alt="macOS 14+" src="https://img.shields.io/badge/macOS-14%2B-b7ff3c?style=flat-square&labelColor=111111">
19-
<img alt="Swift 6" src="https://img.shields.io/badge/Swift-6-b7ff3c?style=flat-square&labelColor=111111">
18+
<img alt="macOS 13.5+" src="https://img.shields.io/badge/macOS-13.5%2B-b7ff3c?style=flat-square&labelColor=111111">
19+
<img alt="Swift 5.9" src="https://img.shields.io/badge/Swift-5.9-b7ff3c?style=flat-square&labelColor=111111">
2020
<a href="LICENSE"><img alt="MIT 라이선스" src="https://img.shields.io/badge/License-MIT-b7ff3c?style=flat-square&labelColor=111111"></a>
2121
</p>
2222

@@ -44,7 +44,8 @@ Capsomnia 자체는 네트워크 요청을 보내지 않고, 텔레메트리를
4444

4545
필요한 환경:
4646

47-
- macOS 14 이상을 실행하는 Apple silicon Mac
47+
- 업스트림 서명 패키지: macOS 14 이상을 실행하는 Apple silicon Mac
48+
- 이 포크의 소스 설치: macOS 13.5 이상을 실행하는 Intel Mac
4849
- 설치할 때 사용할 관리자 권한
4950

5051
서명된 패키지 설치 방법:
@@ -58,16 +59,18 @@ Capsomnia 자체는 네트워크 요청을 보내지 않고, 텔레메트리를
5859

5960
## 소스에서 빌드하기
6061

61-
개발자용 소스 설치도 지원하며 Swift 6 툴체인이 필요합니다.
62+
개발자용 소스 설치에는 Xcode 15에 포함된 Swift 5.9 툴체인이 필요합니다.
6263

6364
```sh
64-
git clone https://github.com/fuji-mak/Capsomnia.git
65+
git clone --branch macos13-intel https://github.com/hubeen/Capsomnia.git
6566
cd Capsomnia
6667
./scripts/install.sh
6768
```
6869

6970
소스 설치 프로그램은 `Capsomnia.app`을 로컬에서 빌드해 `~/Applications/`에 넣고, 같은 helper와 sudoers 규칙을 설치한 뒤 사용자 LaunchAgent를 시작합니다.
7071

72+
소유권 규칙과 필수 Intel 하드웨어 체크리스트는 [`INTEL-MACOS13-VALIDATION.md`](INTEL-MACOS13-VALIDATION.md)를 참고하세요.
73+
7174
## 작동 방식
7275

7376
- 대문자 고정 방지(선택 사항): Capsomnia가 켜져 있을 때 입력이 대문자로 고정되지 않도록 합니다. Shift를 누른 대문자 입력은 그대로 사용할 수 있습니다.

README.md

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515

1616
<p align="center">
1717
<a href="https://github.com/fuji-mak/Capsomnia/actions/workflows/ci.yml"><img alt="CI" src="https://img.shields.io/github/actions/workflow/status/fuji-mak/Capsomnia/ci.yml?branch=main&style=flat-square&label=CI&labelColor=111111&color=b7ff3c"></a>
18-
<img alt="macOS 14+" src="https://img.shields.io/badge/macOS-14%2B-b7ff3c?style=flat-square&labelColor=111111">
19-
<img alt="Swift 6" src="https://img.shields.io/badge/Swift-6-b7ff3c?style=flat-square&labelColor=111111">
18+
<img alt="macOS 13.5+" src="https://img.shields.io/badge/macOS-13.5%2B-b7ff3c?style=flat-square&labelColor=111111">
19+
<img alt="Swift 5.9" src="https://img.shields.io/badge/Swift-5.9-b7ff3c?style=flat-square&labelColor=111111">
2020
<a href="LICENSE"><img alt="MIT License" src="https://img.shields.io/badge/License-MIT-b7ff3c?style=flat-square&labelColor=111111"></a>
2121
</p>
2222

@@ -44,7 +44,8 @@ Capsomnia itself does not make network requests, collect telemetry, or require a
4444

4545
Requirements:
4646

47-
- Apple silicon Mac with macOS 14 or later
47+
- Signed upstream package: Apple silicon Mac with macOS 14 or later
48+
- This fork's source install: Intel Mac with macOS 13.5 or later
4849
- Administrator access during installation
4950

5051
Install the signed package:
@@ -58,16 +59,18 @@ The package build and install scripts are public in [`scripts/build-pkg.sh`](scr
5859

5960
## Build From Source
6061

61-
Developer source install still works and requires a Swift 6 toolchain:
62+
Developer source install requires the Swift 5.9 toolchain included with Xcode 15:
6263

6364
```sh
64-
git clone https://github.com/fuji-mak/Capsomnia.git
65+
git clone --branch macos13-intel https://github.com/hubeen/Capsomnia.git
6566
cd Capsomnia
6667
./scripts/install.sh
6768
```
6869

6970
The source installer builds `Capsomnia.app` locally, places it in `~/Applications/`, installs the same helper and sudoers rule, and starts a user LaunchAgent.
7071

72+
See [`INTEL-MACOS13-VALIDATION.md`](INTEL-MACOS13-VALIDATION.md) for ownership rules and the required Intel hardware checklist.
73+
7174
## What It Does
7275

7376
- Prevent all-caps typing (optional): when Capsomnia is on, Caps Lock no longer forces uppercase input. Shift still types uppercase letters.

README.zh-Hans.md

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515

1616
<p align="center">
1717
<a href="https://github.com/fuji-mak/Capsomnia/actions/workflows/ci.yml"><img alt="CI" src="https://img.shields.io/github/actions/workflow/status/fuji-mak/Capsomnia/ci.yml?branch=main&style=flat-square&label=CI&labelColor=111111&color=b7ff3c"></a>
18-
<img alt="macOS 14+" src="https://img.shields.io/badge/macOS-14%2B-b7ff3c?style=flat-square&labelColor=111111">
19-
<img alt="Swift 6" src="https://img.shields.io/badge/Swift-6-b7ff3c?style=flat-square&labelColor=111111">
18+
<img alt="macOS 13.5+" src="https://img.shields.io/badge/macOS-13.5%2B-b7ff3c?style=flat-square&labelColor=111111">
19+
<img alt="Swift 5.9" src="https://img.shields.io/badge/Swift-5.9-b7ff3c?style=flat-square&labelColor=111111">
2020
<a href="LICENSE"><img alt="MIT 许可证" src="https://img.shields.io/badge/License-MIT-b7ff3c?style=flat-square&labelColor=111111"></a>
2121
</p>
2222

@@ -46,7 +46,8 @@ Capsomnia本身不会发起网络请求、收集遥测数据,也不需要账
4646

4747
要求:
4848

49-
- 搭载Apple芯片、运行macOS 14或更高版本的Mac
49+
- 上游签名软件包:运行macOS 14或更高版本的Apple芯片Mac
50+
- 此分支的源代码安装:运行macOS 13.5或更高版本的Intel Mac
5051
- 安装时拥有管理员权限
5152

5253
安装已签名的软件包:
@@ -60,16 +61,18 @@ Capsomnia本身不会发起网络请求、收集遥测数据,也不需要账
6061

6162
## 从源代码构建
6263

63-
开发者仍可从源代码安装,需要Swift 6工具链
64+
源代码安装需要Xcode 15附带的Swift 5.9工具链
6465

6566
```sh
66-
git clone https://github.com/fuji-mak/Capsomnia.git
67+
git clone --branch macos13-intel https://github.com/hubeen/Capsomnia.git
6768
cd Capsomnia
6869
./scripts/install.sh
6970
```
7071

7172
源代码安装器会在本地构建`Capsomnia.app`,将其放入`~/Applications/`,安装同样的辅助程序和sudoers规则,并启动用户LaunchAgent。
7273

74+
所有权规则和必需的 Intel 硬件检查清单请参阅 [`INTEL-MACOS13-VALIDATION.md`](INTEL-MACOS13-VALIDATION.md)
75+
7376
## 功能
7477

7578
- 防止输入锁定为大写(可选):Capsomnia开启时,防止输入被锁定为大写。仍可按住Shift输入大写字母。

Tests/CapsomniaTests/SettingsWindowControllerTests.swift

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ final class SettingsWindowControllerTests: XCTestCase {
2323
let contentView = try XCTUnwrap(controller.window?.contentView)
2424
contentView.layoutSubtreeIfNeeded()
2525

26-
var renderedText = Set(visibleDescendants(of: contentView).map(\NSTextField.stringValue))
26+
var renderedText = Set(
27+
(visibleDescendants(of: contentView) as [NSTextField]).map(\.stringValue)
28+
)
2729
XCTAssertTrue(renderedText.contains(strings.initialPreferencesHeading))
2830
XCTAssertTrue(renderedText.contains(strings.dedicatedCapsLockMode))
2931
XCTAssertFalse(renderedText.contains(strings.preferencesHeading))
@@ -39,7 +41,9 @@ final class SettingsWindowControllerTests: XCTestCase {
3941
controller.show(page: .settings)
4042
contentView.layoutSubtreeIfNeeded()
4143

42-
renderedText = Set(visibleDescendants(of: contentView).map(\NSTextField.stringValue))
44+
renderedText = Set(
45+
(visibleDescendants(of: contentView) as [NSTextField]).map(\.stringValue)
46+
)
4347
XCTAssertTrue(renderedText.contains(strings.preferencesHeading))
4448
XCTAssertFalse(renderedText.contains(strings.displaySleepOnLidClose))
4549
XCTAssertFalse(renderedText.contains(strings.openAtLogin))
@@ -153,7 +157,7 @@ final class SettingsWindowControllerTests: XCTestCase {
153157
XCTAssertEqual(advancedWindow.title, strings.advancedSettings)
154158

155159
let renderedText = Set(
156-
visibleDescendants(of: contentView).map(\NSTextField.stringValue)
160+
(visibleDescendants(of: contentView) as [NSTextField]).map(\.stringValue)
157161
)
158162
for expected in [
159163
strings.advancedSettings,

resources/Info.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
<string>2.0.4</string>
3535

3636
<key>LSMinimumSystemVersion</key>
37-
<string>14.0</string>
37+
<string>13.5</string>
3838

3939
<key>LSUIElement</key>
4040
<true/>

0 commit comments

Comments
 (0)