Skip to content

Commit ece3845

Browse files
authored
feat: migrate from Xcode project to Swift Package Manager (#115)
1 parent 162ccff commit ece3845

File tree

200 files changed

+1447
-2747
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

200 files changed

+1447
-2747
lines changed

β€Ž.github/workflows/dev-build.ymlβ€Ž

Lines changed: 20 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,18 @@ on:
99
branches: [main]
1010
paths:
1111
- "**.swift"
12-
- "**.xcodeproj/**"
13-
- "**.xcworkspace/**"
14-
- "Assets.xcassets/**"
12+
- "Package.swift"
13+
- "Sources/**"
14+
- "Tests/**"
15+
- "Scripts/**"
1516
- ".github/workflows/dev-build.yml"
1617
pull_request:
1718
paths:
1819
- "**.swift"
19-
- "**.xcodeproj/**"
20-
- "**.xcworkspace/**"
21-
- "Assets.xcassets/**"
20+
- "Package.swift"
21+
- "Sources/**"
22+
- "Tests/**"
23+
- "Scripts/**"
2224
- ".github/workflows/dev-build.yml"
2325
workflow_dispatch:
2426

@@ -34,45 +36,29 @@ jobs:
3436
- name: Select Xcode version
3537
run: sudo xcode-select -s /Applications/Xcode_26.2.app/Contents/Developer
3638

37-
- name: Show Xcode version
38-
run: xcodebuild -version
39+
- name: Show Swift version
40+
run: swift --version
3941

4042
- name: Get short SHA
4143
id: slug
4244
run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
4345

4446
- name: Build app
47+
env:
48+
KASET_SIGNING: adhoc
49+
ARCHES: "arm64 x86_64"
4550
run: |
46-
xcodebuild -project Kaset.xcodeproj \
47-
-scheme Kaset \
48-
-configuration Release \
49-
-derivedDataPath ./build \
50-
-destination 'platform=macOS' \
51-
CODE_SIGN_IDENTITY="" \
52-
CODE_SIGNING_REQUIRED=NO \
53-
CODE_SIGNING_ALLOWED=NO \
54-
ARCHS="arm64 x86_64" \
55-
ONLY_ACTIVE_ARCH=NO \
56-
MARKETING_VERSION="0.0.0-dev.${{ steps.slug.outputs.sha_short }}" \
57-
CURRENT_PROJECT_VERSION="${{ github.run_number }}"
51+
# Override version for dev builds
52+
echo "MARKETING_VERSION=0.0.0-dev.${{ steps.slug.outputs.sha_short }}" > version.env
53+
echo "BUILD_NUMBER=${{ github.run_number }}" >> version.env
54+
Scripts/build-app.sh release
5855
5956
- name: Create DMG
6057
run: |
61-
APP_NAME="Kaset.app"
62-
BUILD_DIR="./build/Build/Products/Release"
58+
APP_PATH=".build/app/Kaset.app"
6359
64-
# debug: show what's in the build folder
65-
echo "Contents of $BUILD_DIR:"
66-
ls -la "$BUILD_DIR" || true
67-
68-
# clean previous artifacts
69-
rm -rf dmg_contents kaset-dev.dmg
70-
71-
# find the main app explicitly by name
72-
APP_PATH=$(find "$BUILD_DIR" -type d -name "$APP_NAME" | head -n 1)
73-
74-
if [ -z "$APP_PATH" ]; then
75-
echo "Error: Could not find $APP_NAME in $BUILD_DIR"
60+
if [ ! -d "$APP_PATH" ]; then
61+
echo "Error: Could not find Kaset.app"
7662
exit 1
7763
fi
7864

β€Ž.github/workflows/release.ymlβ€Ž

Lines changed: 16 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -38,22 +38,11 @@ jobs:
3838
- name: Select Xcode version
3939
run: sudo xcode-select -s /Applications/Xcode_26.2.app/Contents/Developer
4040

41-
- name: Show Xcode version
42-
run: xcodebuild -version
41+
- name: Show Swift version
42+
run: swift --version
4343

4444
- name: Run unit tests
45-
run: |
46-
set -o pipefail
47-
xcodebuild \
48-
-project Kaset.xcodeproj \
49-
-scheme Kaset \
50-
-destination 'platform=macOS' \
51-
-derivedDataPath ./build \
52-
-only-testing:KasetTests \
53-
CODE_SIGN_IDENTITY="" \
54-
CODE_SIGNING_REQUIRED=NO \
55-
CODE_SIGNING_ALLOWED=NO \
56-
test
45+
run: swift test -q --skip KasetUITests
5746

5847
- name: Determine version from tag
5948
id: version
@@ -69,46 +58,30 @@ jobs:
6958
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
7059
echo "Using version: $VERSION"
7160
72-
- name: Build release configuration
61+
- name: Build release app
62+
env:
63+
KASET_SIGNING: adhoc
64+
ARCHES: "arm64 x86_64"
7365
run: |
74-
xcodebuild -project Kaset.xcodeproj \
75-
-scheme Kaset \
76-
-configuration Release \
77-
-destination 'platform=macOS' \
78-
-derivedDataPath ./build \
79-
CODE_SIGN_IDENTITY="" \
80-
CODE_SIGNING_REQUIRED=NO \
81-
CODE_SIGNING_ALLOWED=NO \
82-
ARCHS="arm64 x86_64" \
83-
ONLY_ACTIVE_ARCH=NO \
84-
MARKETING_VERSION="${{ steps.version.outputs.version }}" \
85-
CURRENT_PROJECT_VERSION="${{ github.run_number }}" \
86-
build
66+
# Set version from tag
67+
echo "MARKETING_VERSION=${{ steps.version.outputs.version }}" > version.env
68+
echo "BUILD_NUMBER=${{ github.run_number }}" >> version.env
69+
Scripts/build-app.sh release
8770
8871
- name: Install create-dmg
8972
run: brew install create-dmg
9073

9174
- name: Create DMG
9275
run: |
93-
APP_NAME="Kaset.app"
94-
BUILD_DIR="./build/Build/Products/Release"
76+
APP_PATH=".build/app/Kaset.app"
9577
96-
# debug: show what's in the build folder
97-
echo "Contents of $BUILD_DIR:"
98-
ls -la "$BUILD_DIR" || true
99-
100-
# clean previous artifact
101-
rm -f kaset.dmg || true
102-
103-
# find the main app explicitly by name to avoid Sparkle Updater.app
104-
APP_PATH=$(find "$BUILD_DIR" -type d -name "$APP_NAME" | head -n 1)
105-
106-
if [ -z "$APP_PATH" ]; then
107-
echo "Error: Could not find $APP_NAME in $BUILD_DIR"
78+
if [ ! -d "$APP_PATH" ]; then
79+
echo "Error: Could not find Kaset.app"
10880
exit 1
10981
fi
11082
11183
echo "Found app at: $APP_PATH"
84+
rm -f kaset.dmg || true
11285
11386
# Verify app version matches expected version
11487
PLIST_VERSION=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "$APP_PATH/Contents/Info.plist")
@@ -192,7 +165,7 @@ jobs:
192165
fi
193166
194167
# Find Sparkle's sign_update binary
195-
SIGN_UPDATE=$(find ./build -name "sign_update" -type f 2>/dev/null | head -1)
168+
SIGN_UPDATE=$(find ./.build -name "sign_update" -type f 2>/dev/null | head -1)
196169
if [ -z "$SIGN_UPDATE" ]; then
197170
echo "Warning: sign_update not found, skipping Sparkle signing"
198171
echo "signature=" >> "$GITHUB_OUTPUT"

β€Ž.github/workflows/tests.ymlβ€Ž

Lines changed: 23 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,17 @@ on:
99
branches: [main]
1010
paths:
1111
- "**.swift"
12-
- "**.xcodeproj/**"
13-
- "**.xcworkspace/**"
12+
- "Package.swift"
13+
- "Sources/**"
14+
- "Tests/**"
1415
- ".github/workflows/tests.yml"
1516
pull_request:
1617
branches: [main]
1718
paths:
1819
- "**.swift"
19-
- "**.xcodeproj/**"
20-
- "**.xcworkspace/**"
20+
- "Package.swift"
21+
- "Sources/**"
22+
- "Tests/**"
2123
- ".github/workflows/tests.yml"
2224
workflow_dispatch:
2325

@@ -33,30 +35,11 @@ jobs:
3335
- name: Select Xcode version
3436
run: sudo xcode-select -s /Applications/Xcode_26.2.app/Contents/Developer
3537

36-
- name: Show Xcode version
37-
run: xcodebuild -version
38+
- name: Show Swift version
39+
run: swift --version
3840

3941
- name: Run unit tests
40-
run: |
41-
set -o pipefail
42-
xcodebuild \
43-
-project Kaset.xcodeproj \
44-
-scheme Kaset \
45-
-destination 'platform=macOS' \
46-
-derivedDataPath ./build \
47-
-only-testing:KasetTests \
48-
CODE_SIGN_IDENTITY="" \
49-
CODE_SIGNING_REQUIRED=NO \
50-
CODE_SIGNING_ALLOWED=NO \
51-
test
52-
53-
- name: Upload test results on failure
54-
if: failure()
55-
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v4
56-
with:
57-
name: macOS-TestResults
58-
path: ./build/*.xcresult
59-
retention-days: 7
42+
run: swift test -q --skip KasetUITests
6043

6144
macos_ui_tests:
6245
name: macOS UI Tests
@@ -72,16 +55,26 @@ jobs:
7255
- name: Show Xcode version
7356
run: xcodebuild -version
7457

58+
- name: Build app bundle for UI tests
59+
env:
60+
KASET_SIGNING: adhoc
61+
run: Scripts/build-app.sh debug
62+
63+
- name: Install app for UI tests
64+
run: |
65+
sudo cp -R .build/app/Kaset.app /Applications/Kaset.app
66+
# Register with Launch Services so XCUIApplication can find it
67+
/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister -f /Applications/Kaset.app
68+
7569
- name: Run UI tests
7670
run: |
7771
set -o pipefail
7872
xcodebuild \
79-
-project Kaset.xcodeproj \
80-
-scheme Kaset \
73+
-project KasetUITests.xcodeproj \
74+
-scheme KasetUITests \
8175
-destination 'platform=macOS' \
8276
-derivedDataPath ./build \
83-
-only-testing:KasetUITests \
84-
CODE_SIGN_IDENTITY="" \
77+
CODE_SIGN_IDENTITY="-" \
8578
CODE_SIGNING_REQUIRED=NO \
8679
CODE_SIGNING_ALLOWED=NO \
8780
test

β€Ž.gitignoreβ€Ž

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ DerivedData/
1010
*.dSYM
1111
*.profraw
1212

13-
# Xcode user data
13+
# Xcode (auto-generated from Package.swift)
14+
*.xcodeproj/
15+
!KasetUITests.xcodeproj/
16+
*.xcworkspace/
1417
*.xcuserdata/
1518
xcuserdata/
1619

17-
# Xcode schemes
18-
!*.xcscheme
19-
2020
# Swift Package Manager
2121
/.swiftpm
2222
Packages/
@@ -57,4 +57,5 @@ Thumbs.db
5757
.env.local
5858
*.pem
5959

60-
TEMP_*
60+
TEMP_*
61+
build/

β€Ž.swiftlint.ymlβ€Ž

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,14 @@ opt_in_rules:
2626
- yoda_condition
2727

2828
included:
29-
- App
30-
- Core
31-
- Views
29+
- Sources
3230
- Tests
3331

3432
excluded:
3533
- .build
3634
- DerivedData
3735
- Packages
36+
- Sources/APIExplorer
3837

3938
# Rules configuration
4039
identifier_name:
@@ -81,7 +80,7 @@ custom_rules:
8180
message: "Use Swift concurrency (async/await, MainActor) instead of DispatchQueue"
8281
severity: error
8382
excluded:
84-
- "Core/Services/NetworkMonitor.swift" # NWPathMonitor requires DispatchQueue
83+
- "Sources/Kaset/Services/NetworkMonitor.swift" # NWPathMonitor requires DispatchQueue
8584

8685
no_print:
8786
name: "No print statements"

β€ŽAGENTS.mdβ€Ž

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,18 @@ Kaset is a native macOS YouTube Music client (Swift/SwiftUI) using a hidden WebV
1818
1919
> ⚠️ **Prefer API over WebView** β€” Always use `YTMusicClient` API calls when functionality exists. Only use WebView for playback (DRM-protected audio) and authentication.
2020
21-
> πŸ”§ **Improve API Explorer, Don't Write One-Off Scripts** β€” When exploring or debugging API-related functionality, **always enhance `Tools/api-explorer.swift`** instead of writing temporary scripts.
21+
> πŸ”§ **Improve API Explorer, Don't Write One-Off Scripts** β€” When exploring or debugging API-related functionality, **always enhance `Sources/APIExplorer/main.swift`** instead of writing temporary scripts.
2222
2323
> πŸ“ **Document Architectural Decisions** β€” For significant design changes, create an ADR in `docs/adr/`.
2424
2525
## Build & Code Quality
2626

2727
```bash
2828
# Build
29-
xcodebuild -scheme Kaset -destination 'platform=macOS' build
29+
swift build
3030

3131
# Unit Tests (never combine with UI tests)
32-
xcodebuild -scheme Kaset -destination 'platform=macOS' test -only-testing:KasetTests
32+
swift test --skip KasetUITests
3333

3434
# Lint & Format
3535
swiftlint --strict && swiftformat .
@@ -43,12 +43,12 @@ swiftlint --strict && swiftformat .
4343
4444
## API Discovery
4545

46-
> ⚠️ **MANDATORY**: Before implementing ANY feature that requires a new or modified API call, you MUST explore the endpoint first using `./Tools/api-explorer.swift`. Do NOT guess or assume API response structures.
46+
> ⚠️ **MANDATORY**: Before implementing ANY feature that requires a new or modified API call, you MUST explore the endpoint first using `swift run api-explorer`. Do NOT guess or assume API response structures.
4747
4848
```bash
49-
./Tools/api-explorer.swift auth # Check auth status
50-
./Tools/api-explorer.swift list # List known endpoints
51-
./Tools/api-explorer.swift browse FEmusic_home -v # Explore with verbose output
49+
swift run api-explorer auth # Check auth status
50+
swift run api-explorer list # List known endpoints
51+
swift run api-explorer browse FEmusic_home -v # Explore with verbose output
5252
```
5353

5454
## Coding Rules
@@ -70,4 +70,4 @@ These are project-specific rules that differ from standard Swift/SwiftUI convent
7070

7171
## Task Planning
7272

73-
For non-trivial tasks: **Research β†’ Plan β†’ Get approval β†’ Implement β†’ QA**. Run `xcodebuild build` continuously during implementation. If things go wrong, revert and re-scope rather than patching.
73+
For non-trivial tasks: **Research β†’ Plan β†’ Get approval β†’ Implement β†’ QA**. Run `swift build` continuously during implementation. If things go wrong, revert and re-scope rather than patching.

β€ŽApp/Kaset.Debug.entitlementsβ€Ž

Lines changed: 0 additions & 12 deletions
This file was deleted.

0 commit comments

Comments
Β (0)