mcpField
M
macos-spm-app-packaging

macos-spm-app-packaging

github.com/patrickserrano开发1354 次安装41 周前更新
代码仓库

关于 macos-spm-app-packaging

此技能用于在不使用 Xcode 的情况下搭建、构建、打包和发布 SwiftPM macOS 应用。它涵盖应用包、代码签名、公证及可选的 Sparkle 更新元数据。

主要功能

  1. 01搭建 SwiftPM 应用项目
  2. 02使用 SwiftPM 构建和测试
  3. 03打包 macOS .app 应用包
  4. 04为开发或发布版本签名
  5. 05指导公证和发布分发
  6. 06可选生成 Sparkle appcast 条目

适用场景

  1. 01不使用 Xcode 开始开发 macOS 应用
  2. 02将 SwiftPM 构建结果打包为应用
  3. 03准备签名并经过公证的发布版本
  4. 04发布带版本号的 GitHub 版本

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/patrickserrano/skills --skill macos-spm-app-packaging

安装后在 Claude Code 中输入/macos-spm-app-packaging确认已加载。

详细介绍

macOS SwiftPM 应用打包

概述

引导搭建完整的 SwiftPM macOS 应用,然后无需 Xcode 即可构建、打包并运行。本指南涵盖从项目脚手架搭建到发布分发的完整工作流。

项目脚手架搭建

基本结构
MyApp/
├── Package.swift
├── Sources/
│   └── MyApp/
│       ├── MyApp.swift          # @main App entry
│       └── ContentView.swift
├── Resources/
│   ├── Assets.xcassets/
│   └── Info.plist
├── Scripts/
│   ├── package_app.sh
│   ├── compile_and_run.sh
│   └── sign-and-notarize.sh
└── version.env
Package.swift
// swift-tools-version: 5.9
import PackageDescription

let package = Package(
    name: "MyApp",
    platforms: [.macOS(.v14)],
    products: [
        .executable(name: "MyApp", targets: ["MyApp"])
    ],
    targets: [
        .executableTarget(
            name: "MyApp",
            resources: [
                .process("Resources")
            ]
        )
    ]
)
version.env
APP_NAME="MyApp"
BUNDLE_ID="com.example.myapp"
VERSION="1.0.0"
BUILD_NUMBER="1"
MIN_MACOS="14.0"
# Set to 1 for menu bar apps
MENU_BAR_APP=0

构建和运行

使用 SwiftPM 构建
# Debug build
swift build

# Release build
swift build -c release

# Run tests
swift test
打包为 .app Bundle

创建 Scripts/package_app.sh:

#!/bin/bash
set -e

source version.env

BUILD_DIR=".build/release"
APP_BUNDLE="$BUILD_DIR/$APP_NAME.app"
CONTENTS="$APP_BUNDLE/Contents"
MACOS="$CONTENTS/MacOS"
RESOURCES="$CONTENTS/Resources"

# Build release
swift build -c release

# Create bundle structure
rm -rf "$APP_BUNDLE"
mkdir -p "$MACOS" "$RESOURCES"

# Copy binary
cp "$BUILD_DIR/$APP_NAME" "$MACOS/"

# Copy resources
cp -r Resources/* "$RESOURCES/" 2>/dev/null || true

# Generate Info.plist
cat > "$CONTENTS/Info.plist" << EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>CFBundleExecutable</key>
    <string>$APP_NAME</string>
    <key>CFBundleIdentifier</key>
    <string>$BUNDLE_ID</string>
    <key>CFBundleName</key>
    <string>$APP_NAME</string>
    <key>CFBundleVersion</key>
    <string>$BUILD_NUMBER</string>
    <key>CFBundleShortVersionString</key>
    <string>$VERSION</string>
    <key>LSMinimumSystemVersion</key>
    <string>$MIN_MACOS</string>
    <key>CFBundlePackageType</key>
    <string>APPL</string>
$([ "$MENU_BAR_APP" = "1" ] && echo "    <key>LSUIElement</key>
    <true/>")
</dict>
</plist>
EOF

echo "Created $APP_BUNDLE"
开发运行脚本

创建 Scripts/compile_and_run.sh:

#!/bin/bash
set -e

source version.env

# Kill existing instance
pkill -x "$APP_NAME" 2>/dev/null || true

# Package
./Scripts/package_app.sh

# Launch
open ".build/release/$APP_NAME.app"

代码签名

开发签名
# Sign for local development
codesign --force --sign - ".build/release/MyApp.app"

# Or with a specific identity
codesign --force --sign "Developer ID Application: Your Name" ".build/release/MyApp.app"
创建稳定的开发身份
# Generate self-signed certificate for consistent dev signing
security create-keychain -p "" dev-signing.keychain
security default-keychain -s dev-signing.keychain
# Follow prompts in Keychain Access to create certificate

公证和发布

创建 Scripts/sign-and-notarize.sh:

#!/bin/bash
set -e

source version.env

APP_PATH=".build/release/$APP_NAME.app"
ZIP_PATH=".build/release/$APP_NAME-$VERSION.zip"

# Sign with Developer ID
codesign --force --options runtime --sign "Developer ID Application: Your Name" "$APP_PATH"

# Create zip for notarization
ditto -c -k --keepParent "$APP_PATH" "$ZIP_PATH"

# Submit for notarization
xcrun notarytool submit "$ZIP_PATH" \
    --apple-id "[email protected]" \
    --team-id "TEAM_ID" \
    --password "@keychain:AC_PASSWORD" \
    --wait

# Staple the ticket
xcrun stapler staple "$APP_PATH"

# Re-zip with stapled ticket
rm "$ZIP_PATH"
ditto -c -k --keepParent "$APP_PATH" "$ZIP_PATH"

echo "Release ready: $ZIP_PATH"

Sparkle 更新(可选)

生成 Appcast 条目
#!/bin/bash
source version.env

ZIP_PATH=".build/release/$APP_NAME-$VERSION.zip"
SIZE=$(stat -f%z "$ZIP_PATH")
SIGNATURE=$(./bin/sign_update "$ZIP_PATH")
DATE=$(date -R)

cat << EOF
<item>
    <title>Version $VERSION</title>
    <pubDate>$DATE</pubDate>
    <sparkle:version>$BUILD_NUMBER</sparkle:version>
    <sparkle:shortVersionString>$VERSION</sparkle:shortVersionString>
    <enclosure
        url="https://example.com/releases/$APP_NAME-$VERSION.zip"
        length="$SIZE"
        type="application/octet-stream"
        sparkle:edSignature="$SIGNATURE"
    />
</item>
EOF

GitHub 发布

# Create tag
git tag -a "v$VERSION" -m "Release $VERSION"
git push origin "v$VERSION"

# Create GitHub release
gh release create "v$VERSION" \
    ".build/release/$APP_NAME-$VERSION.zip" \
    --title "v$VERSION" \
    --notes "Release notes here"

检查清单

脚手架搭建
  • Package.swift 包含正确的目标和资源
  • version.env 包含应用元数据
  • Info.plist 模板或生成脚本
  • 基本应用入口点(@main App)
构建
  • swift build 成功
  • swift test 通过
  • 资源已正确复制
打包
  • .app Bundle 结构正确
  • Info.plist 已生成且值正确
  • 应用可从 Finder 启动
发布
  • 已使用 Developer ID 进行代码签名
  • 已公证并装订票据
  • 已创建用于分发的 Zip
  • (可选)已更新 Sparkle appcast

评论

支持 Markdown