Skip to content

Commit 8fd4ce1

Browse files
committed
updated Claude.md
1 parent e0947fe commit 8fd4ce1

2 files changed

Lines changed: 40 additions & 10 deletions

File tree

CLAUDE.md

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
44

55
## Project Overview
66

7-
`quickpkg` is a macOS command-line tool that builds installer packages (.pkg files) from applications. It wraps Apple's `pkgbuild` tool and automatically extracts app metadata (name, version, identifier) to create properly named packages.
7+
`quickpkg` is a macOS command-line tool that builds installer packages (.pkg files) from applications. It wraps Apple's `pkgbuild` and `productbuild` tools and automatically extracts app metadata (name, version, identifier, minimum OS version) to create properly named packages.
88

99
Two implementations exist:
10-
- **Swift** (`Sources/quickpkg/`) - Primary implementation (macOS 15+) using swift-argument-parser and swift-subprocess
10+
- **Swift** (`Sources/quickpkg/`) - Primary implementation (macOS 15+, Swift 6.0) using swift-argument-parser and swift-subprocess
1111
- **Python** (`quickpkg`) - Original implementation using MacAdmins Managed Python
1212

1313
## Building and Running
@@ -25,30 +25,60 @@ swift build -c release
2525

2626
# Test scripts option
2727
.build/debug/quickpkg /Applications/SomeApp.app --scripts testscripts/
28+
29+
# Build distribution package (default)
30+
.build/debug/quickpkg /Applications/SomeApp.app
31+
32+
# Build component package
33+
.build/debug/quickpkg /Applications/SomeApp.app --component
2834
```
2935

3036
## Architecture
3137

3238
The Swift implementation in `Sources/quickpkg/` follows this flow:
3339

3440
1. `QuickPkg.swift` - Entry point using `AsyncParsableCommand`, parses CLI args
35-
2. `InputType.swift` - Detects input type from file extension (app, dmg, zip, xip)
41+
2. `InputType.swift` - Detects input type from file extension (app, dmg, zip, xip); also defines `Ownership`, `Compression`, and `PackageType` enums
3642
3. For archives:
3743
- `DMGManager.swift` - Actor for mounting/unmounting DMGs via `hdiutil`
38-
- `ArchiveExtractor.swift` - Handles zip/xip extraction
39-
4. `AppMetadata.swift` - Extracts name/version/identifier from app's `Info.plist`
40-
5. `PackageBuilder.swift` - Runs `pkgbuild --analyze` then `pkgbuild` to create the pkg
41-
6. `PlistHandler.swift` - Modifies component plist for non-relocatable packages (default)
44+
- `ArchiveExtractor.swift` - Handles zip/xip extraction (Sendable)
45+
4. `AppMetadata.swift` - Extracts name/version/identifier/minimumSystemVersion using Bundle API
46+
5. `PackageBuilder.swift` - Runs `pkgbuild --analyze`, removes quarantine attributes, then `pkgbuild` (and `productbuild` for distribution packages)
47+
6. `PlistHandler.swift` - Modifies component plist for non-relocatable packages (Sendable)
4248

4349
Supporting modules:
44-
- `ShellExecutor.swift` - Async subprocess execution wrapper
50+
- `ShellExecutor.swift` - Async subprocess execution wrapper using StringOutput (Sendable)
4551
- `TempDirectory.swift` - Manages temp directories with cleanup
4652
- `Logger.swift` - Verbosity-aware logging (levels 1-3)
47-
- `QuickPkgError.swift` - Error types
53+
- `QuickPkgError.swift` - Error types with LocalizedError conformance
54+
- `URLExtension.swift` - URL extension with `fileExists` property
4855

4956
## Key Design Notes
5057

51-
- Packages are **non-relocatable by default** (installer won't search for moved apps)
58+
- **Distribution packages by default** - Uses `productbuild` to wrap the component package (use `--component` for component-only)
59+
- **Non-relocatable by default** - Installer won't search for moved apps (use `--relocatable` to change)
60+
- **Automatic min-os-version** - Extracts `LSMinimumSystemVersion` from app bundle and passes to pkgbuild
61+
- **Quarantine removal** - Removes `com.apple.quarantine` xattr from payload before packaging
5262
- Output naming: `{name}-{version}.pkg` with placeholders `{name}`, `{version}`, `{identifier}`
5363
- DMGManager is an actor to safely manage mount/unmount lifecycle
5464
- All shell commands go through ShellExecutor which logs at verbosity level 3
65+
- Types marked `Sendable` for Swift 6 concurrency safety: `ShellExecutor`, `PackageBuilder`, `PlistHandler`, `ArchiveExtractor`, `Logger`
66+
67+
## Command-Line Options
68+
69+
Key options beyond the Python version:
70+
- `--compression <latest|legacy>` - Compression type for pkgbuild
71+
- `--component` / `--distribution` - Package type (distribution is default)
72+
- `--min-os-version` is automatically extracted from the app's Info.plist
73+
74+
## Scripts
75+
76+
- `pkgAndNotarize.sh` - Build, sign, package, notarize, and staple workflow for creating signed releases
77+
78+
## Code Patterns
79+
80+
- Use `URL(filePath:)` not the deprecated `URL(fileURLWithPath:)`
81+
- Use `url.fileExists` extension instead of `FileManager.default.fileExists(atPath:)`
82+
- Use `Bundle` API for reading app metadata (see `AppMetadata.swift`)
83+
- Use `ArgumentHelp(abstract:discussion:)` for multi-line help text
84+
- Use `EnumerableFlag` for mutually exclusive flags like `--component`/`--distribution`

0 commit comments

Comments
 (0)