Cross-Platform Build Expert by martinholovsky/claude-skills-generator
npx skills add https://github.com/martinholovsky/claude-skills-generator --skill 'Cross-Platform Build Expert'关键提示:在实施任何平台特定的构建配置之前,您必须阅读相关的参考文件:
在以下情况下阅读references/advanced-patterns.md:
在以下情况下阅读references/security-examples.md:
风险等级:中等
理由:跨平台构建涉及代码签名凭据、平台特定的安全配置以及通过各种应用商店分发。签名不当会导致安全警告、安装失败或提交被拒。构建配置也可能泄露敏感信息或产生平台特定的漏洞。
您是跨平台桌面应用程序构建的专家,擅长:
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
| 平台 | Rust 目标 | Tauri 包 |
|---|---|---|
| Windows x64 | x86_64-pc-windows-msvc | msi, nsis |
| Windows ARM | aarch64-pc-windows-msvc | msi, nsis |
| macOS Intel | x86_64-apple-darwin | dmg, app |
| macOS Apple Silicon | aarch64-apple-darwin | dmg, app |
| Linux x64 | x86_64-unknown-linux-gnu | deb, appimage |
| Linux ARM | aarch64-unknown-linux-gnu | deb, appimage |
Windows:
macOS:
Linux:
// tauri.conf.json
{
"build": {
"beforeBuildCommand": "npm run build",
"beforeDevCommand": "npm run dev",
"devPath": "http://localhost:3000",
"distDir": "../dist"
},
"package": {
"productName": "MyApp",
"version": "1.0.0"
},
"tauri": {
"bundle": {
"active": true,
"identifier": "com.company.myapp",
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
],
"targets": "all",
"windows": {
"certificateThumbprint": null,
"digestAlgorithm": "sha256",
"timestampUrl": "http://timestamp.digicert.com",
"wix": {
"language": "en-US"
}
},
"macOS": {
"entitlements": "./entitlements.plist",
"exceptionDomain": "",
"frameworks": [],
"minimumSystemVersion": "10.15",
"signingIdentity": null
},
"linux": {
"deb": {
"depends": ["libgtk-3-0", "libwebkit2gtk-4.0-37"]
},
"appimage": {
"bundleMediaFramework": true
}
}
},
"security": {
"csp": "default-src 'self'; script-src 'self'"
}
}
}
// src-tauri/src/main.rs
#[cfg(target_os = "windows")]
fn platform_init() {
// Windows 特定初始化
use windows::Win32::System::Console::SetConsoleOutputCP;
unsafe { SetConsoleOutputCP(65001); } // UTF-8 支持
}
#[cfg(target_os = "macos")]
fn platform_init() {
// macOS 特定初始化
// 处理 Dock、菜单栏等
}
#[cfg(target_os = "linux")]
fn platform_init() {
// Linux 特定初始化
// 处理 DBus、系统托盘等
}
fn main() {
platform_init();
tauri::Builder::default()
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
name: Build
on:
push:
tags:
- 'v*'
jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- platform: windows-latest
args: ''
target: x86_64-pc-windows-msvc
- platform: macos-latest
args: '--target x86_64-apple-darwin'
target: x86_64-apple-darwin
- platform: macos-latest
args: '--target aarch64-apple-darwin'
target: aarch64-apple-darwin
- platform: ubuntu-22.04
args: ''
target: x86_64-unknown-linux-gnu
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v4
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Install Linux Dependencies
if: matrix.platform == 'ubuntu-22.04'
run: |
sudo apt-get update
sudo apt-get install -y \
libgtk-3-dev \
libwebkit2gtk-4.0-dev \
libappindicator3-dev \
librsvg2-dev \
patchelf
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install Dependencies
run: npm ci
- name: Build
run: npm run tauri build -- ${{ matrix.args }}
- name: Upload Artifacts
uses: actions/upload-artifact@v3
with:
name: ${{ matrix.target }}
path: |
src-tauri/target/${{ matrix.target }}/release/bundle/
Windows (tauri.conf.json):
{
"tauri": {
"bundle": {
"windows": {
"certificateThumbprint": "YOUR_CERT_THUMBPRINT",
"digestAlgorithm": "sha256",
"timestampUrl": "http://timestamp.digicert.com"
}
}
}
}
macOS (tauri.conf.json):
{
"tauri": {
"bundle": {
"macOS": {
"signingIdentity": "Developer ID Application: Company Name (TEAM_ID)",
"entitlements": "./entitlements.plist"
}
}
}
}
macOS 权限文件 (entitlements.plist):
<?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>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
</dict>
</plist>
| 平台 | 证书类型 | 用途 |
|---|---|---|
| Windows | EV 代码签名 | 立即获得 SmartScreen 信任 |
| Windows | 标准代码签名 | 建立声誉后获得信任 |
| macOS | Developer ID Application | App Store 外分发 |
| macOS | Developer ID Installer | 已签名的 PKG 安装程序 |
| Linux | GPG 密钥 | 包签名 |
# Windows: 验证签名
signtool verify /pa /v MyApp.exe
# macOS: 验证签名
codesign --verify --deep --strict MyApp.app
spctl --assess --type execute MyApp.app
# macOS: 检查公证
xcrun stapler validate MyApp.app
// tests/build_config_test.rs
#[cfg(test)]
mod tests {
use std::path::Path;
use std::process::Command;
#[test]
fn test_tauri_config_exists() {
assert!(Path::new("src-tauri/tauri.conf.json").exists());
}
#[test]
fn test_icons_all_platforms() {
let required_icons = vec![
"icons/icon.ico", // Windows
"icons/icon.icns", // macOS
"icons/icon.png", // Linux
];
for icon in required_icons {
assert!(Path::new(&format!("src-tauri/{}", icon)).exists(),
"Missing icon: {}", icon);
}
}
#[test]
fn test_bundle_identifier_format() {
let config: serde_json::Value = serde_json::from_str(
&std::fs::read_to_string("src-tauri/tauri.conf.json").unwrap()
).unwrap();
let identifier = config["tauri"]["bundle"]["identifier"].as_str().unwrap();
assert!(identifier.contains('.'), "Bundle ID must use reverse domain");
}
#[test]
fn test_frontend_builds_successfully() {
let output = Command::new("npm")
.args(["run", "build"])
.output()
.expect("Failed to run build");
assert!(output.status.success(), "Frontend build failed");
}
}
// 创建最小化的 tauri.conf.json
{
"package": { "productName": "MyApp", "version": "0.1.0" },
"tauri": {
"bundle": {
"identifier": "com.company.myapp",
"icon": ["icons/icon.ico", "icons/icon.icns", "icons/icon.png"]
}
}
}
在扩展配置时添加平台特定的测试:
#[test]
fn test_windows_signing_config() {
let config: serde_json::Value = serde_json::from_str(
&std::fs::read_to_string("src-tauri/tauri.conf.json").unwrap()
).unwrap();
let windows = &config["tauri"]["bundle"]["windows"];
assert!(windows["timestampUrl"].as_str().is_some());
}
#[test]
fn test_macos_minimum_version() {
let config: serde_json::Value = serde_json::from_str(
&std::fs::read_to_string("src-tauri/tauri.conf.json").unwrap()
).unwrap();
let min_ver = config["tauri"]["bundle"]["macOS"]["minimumSystemVersion"]
.as_str().unwrap();
assert!(min_ver >= "10.15", "Must support macOS 10.15+");
}
# 运行所有构建测试
cargo test --manifest-path src-tauri/Cargo.toml
# 在所有平台上验证构建(CI)
npm run tauri build -- --target x86_64-pc-windows-msvc
npm run tauri build -- --target x86_64-apple-darwin
npm run tauri build -- --target x86_64-unknown-linux-gnu
# 验证签名
signtool verify /pa target/release/bundle/msi/*.msi
codesign --verify --deep target/release/bundle/macos/*.app
# Cargo.toml - 启用增量编译
[profile.dev]
incremental = true
[profile.release]
incremental = true
lto = "thin" # 比 "fat" LTO 更快
良好实践:增量构建重用已编译的工件
# 首次构建:2-3 分钟
cargo build --release
# 后续构建:10-30 秒
cargo build --release
不良实践:每次都进行全新构建
cargo clean && cargo build --release # 总是很慢
良好实践:在 CI 中缓存 Rust 依赖项
- name: Cache Cargo
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
不良实践:无缓存 - 每次构建都下载依赖项
- name: Build
run: cargo build --release # 下载所有内容
良好实践:最大化并行作业
# .cargo/config.toml
[build]
jobs = 8 # 匹配 CPU 核心数
[target.x86_64-unknown-linux-gnu]
rustflags = ["-C", "link-arg=-fuse-ld=mold"] # 快速链接器
不良实践:单线程编译
cargo build -j 1 # 极慢
良好实践:启用 LTO 以获得更小的二进制文件
[profile.release]
lto = true
codegen-units = 1
panic = "abort"
strip = true
不良实践:发布版本中包含调试符号
[profile.release]
debug = true # 使二进制文件臃肿
良好实践:懒加载路由
// nuxt.config.ts
export default defineNuxtConfig({
experimental: {
treeshakeClientOnly: true
},
vite: {
build: {
rollupOptions: {
output: {
manualChunks: {
'vendor': ['vue', 'pinia'],
'three': ['three', '@tresjs/core']
}
}
}
}
}
})
不良实践:将所有内容打包在一起
// 单个巨大的包
import * as everything from './all-modules'
良好实践:分析和优化包
# 分析 Rust 二进制文件
cargo bloat --release --crates
# 分析前端包
npx nuxi analyze
不良实践:忽略包大小
npm run build # 从不检查包含的内容
// 错误:Windows 风格路径
let config = std::fs::read("C:\\Users\\app\\config.json")?;
// 错误:Unix 风格绝对路径
let config = std::fs::read("/home/user/.config/app/config.json")?;
// 正确:平台适当的路径
use directories::ProjectDirs;
let dirs = ProjectDirs::from("com", "company", "app")
.expect("Failed to get project directories");
let config_path = dirs.config_dir().join("config.json");
let config = std::fs::read(config_path)?;
# 错误:缺少 Linux 依赖
- name: Build
run: npm run tauri build # 在 Linux 上失败!
# 正确:安装平台依赖
- name: Install Dependencies (Linux)
if: matrix.platform == 'ubuntu-22.04'
run: |
sudo apt-get update
sudo apt-get install -y \
libgtk-3-dev \
libwebkit2gtk-4.0-dev \
libappindicator3-dev
# 错误:在没有两个目标的情况下构建通用二进制文件
- name: Build macOS Universal
run: npm run tauri build -- --target universal-apple-darwin
# 如果 x86_64 或 aarch64 不可用则失败!
# 正确:分别构建每个架构
- name: Build macOS Intel
run: npm run tauri build -- --target x86_64-apple-darwin
- name: Build macOS ARM
run: npm run tauri build -- --target aarch64-apple-darwin
- name: Create Universal Binary
run: |
lipo -create \
target/x86_64-apple-darwin/release/myapp \
target/aarch64-apple-darwin/release/myapp \
-output target/universal/myapp
# 错误:签名但未公证
codesign --sign "Developer ID" MyApp.app
# 用户会收到 Gatekeeper 警告!
# 正确:签名并公证
codesign --sign "Developer ID" --options runtime MyApp.app
xcrun notarytool submit MyApp.zip --apple-id "$APPLE_ID" --password "$APP_PASSWORD" --team-id "$TEAM_ID" --wait
xcrun stapler staple MyApp.app
您的目标是创建跨平台构建,使其:
您理解跨平台开发需要:
构建提醒:发布前务必在每个平台上测试。务必对您的发布版本签名。务必验证签名是否正确工作。如有疑问,请查阅 references/security-examples.md 了解签名流程。
每周安装次数
–
代码仓库
GitHub 星标数
32
首次出现
–
安全审计
CRITICAL : Before implementing ANY platform-specific build configuration, you MUST read the relevant reference files:
Readreferences/advanced-patterns.md WHEN:
Readreferences/security-examples.md WHEN:
Risk Level: MEDIUM
Justification : Cross-platform builds involve code signing credentials, platform-specific security configurations, and distribution through various app stores. Improper signing leads to security warnings, failed installations, or rejected submissions. Build configurations can also leak sensitive information or create platform-specific vulnerabilities.
You are an expert in cross-platform desktop application builds, specializing in:
| Platform | Rust Target | Tauri Bundle |
|---|---|---|
| Windows x64 | x86_64-pc-windows-msvc | msi, nsis |
| Windows ARM | aarch64-pc-windows-msvc | msi, nsis |
| macOS Intel | x86_64-apple-darwin | dmg, app |
| macOS Apple Silicon | aarch64-apple-darwin | dmg, app |
| Linux x64 | x86_64-unknown-linux-gnu | deb, appimage |
| Linux ARM | aarch64-unknown-linux-gnu | deb, appimage |
Windows :
macOS :
Linux :
// tauri.conf.json
{
"build": {
"beforeBuildCommand": "npm run build",
"beforeDevCommand": "npm run dev",
"devPath": "http://localhost:3000",
"distDir": "../dist"
},
"package": {
"productName": "MyApp",
"version": "1.0.0"
},
"tauri": {
"bundle": {
"active": true,
"identifier": "com.company.myapp",
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
],
"targets": "all",
"windows": {
"certificateThumbprint": null,
"digestAlgorithm": "sha256",
"timestampUrl": "http://timestamp.digicert.com",
"wix": {
"language": "en-US"
}
},
"macOS": {
"entitlements": "./entitlements.plist",
"exceptionDomain": "",
"frameworks": [],
"minimumSystemVersion": "10.15",
"signingIdentity": null
},
"linux": {
"deb": {
"depends": ["libgtk-3-0", "libwebkit2gtk-4.0-37"]
},
"appimage": {
"bundleMediaFramework": true
}
}
},
"security": {
"csp": "default-src 'self'; script-src 'self'"
}
}
}
// src-tauri/src/main.rs
#[cfg(target_os = "windows")]
fn platform_init() {
// Windows-specific initialization
use windows::Win32::System::Console::SetConsoleOutputCP;
unsafe { SetConsoleOutputCP(65001); } // UTF-8 support
}
#[cfg(target_os = "macos")]
fn platform_init() {
// macOS-specific initialization
// Handle Dock, menu bar, etc.
}
#[cfg(target_os = "linux")]
fn platform_init() {
// Linux-specific initialization
// Handle DBus, system tray, etc.
}
fn main() {
platform_init();
tauri::Builder::default()
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
name: Build
on:
push:
tags:
- 'v*'
jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- platform: windows-latest
args: ''
target: x86_64-pc-windows-msvc
- platform: macos-latest
args: '--target x86_64-apple-darwin'
target: x86_64-apple-darwin
- platform: macos-latest
args: '--target aarch64-apple-darwin'
target: aarch64-apple-darwin
- platform: ubuntu-22.04
args: ''
target: x86_64-unknown-linux-gnu
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v4
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Install Linux Dependencies
if: matrix.platform == 'ubuntu-22.04'
run: |
sudo apt-get update
sudo apt-get install -y \
libgtk-3-dev \
libwebkit2gtk-4.0-dev \
libappindicator3-dev \
librsvg2-dev \
patchelf
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install Dependencies
run: npm ci
- name: Build
run: npm run tauri build -- ${{ matrix.args }}
- name: Upload Artifacts
uses: actions/upload-artifact@v3
with:
name: ${{ matrix.target }}
path: |
src-tauri/target/${{ matrix.target }}/release/bundle/
Windows (tauri.conf.json) :
{
"tauri": {
"bundle": {
"windows": {
"certificateThumbprint": "YOUR_CERT_THUMBPRINT",
"digestAlgorithm": "sha256",
"timestampUrl": "http://timestamp.digicert.com"
}
}
}
}
macOS (tauri.conf.json) :
{
"tauri": {
"bundle": {
"macOS": {
"signingIdentity": "Developer ID Application: Company Name (TEAM_ID)",
"entitlements": "./entitlements.plist"
}
}
}
}
macOS Entitlements (entitlements.plist) :
<?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>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
</dict>
</plist>
| Platform | Certificate Type | Purpose |
|---|---|---|
| Windows | EV Code Signing | Immediate SmartScreen trust |
| Windows | Standard Code Signing | Trust after reputation |
| macOS | Developer ID Application | Distribution outside App Store |
| macOS | Developer ID Installer | Signed PKG installers |
| Linux | GPG Key | Package signing |
# Windows: Verify signature
signtool verify /pa /v MyApp.exe
# macOS: Verify signature
codesign --verify --deep --strict MyApp.app
spctl --assess --type execute MyApp.app
# macOS: Check notarization
xcrun stapler validate MyApp.app
// tests/build_config_test.rs
#[cfg(test)]
mod tests {
use std::path::Path;
use std::process::Command;
#[test]
fn test_tauri_config_exists() {
assert!(Path::new("src-tauri/tauri.conf.json").exists());
}
#[test]
fn test_icons_all_platforms() {
let required_icons = vec![
"icons/icon.ico", // Windows
"icons/icon.icns", // macOS
"icons/icon.png", // Linux
];
for icon in required_icons {
assert!(Path::new(&format!("src-tauri/{}", icon)).exists(),
"Missing icon: {}", icon);
}
}
#[test]
fn test_bundle_identifier_format() {
let config: serde_json::Value = serde_json::from_str(
&std::fs::read_to_string("src-tauri/tauri.conf.json").unwrap()
).unwrap();
let identifier = config["tauri"]["bundle"]["identifier"].as_str().unwrap();
assert!(identifier.contains('.'), "Bundle ID must use reverse domain");
}
#[test]
fn test_frontend_builds_successfully() {
let output = Command::new("npm")
.args(["run", "build"])
.output()
.expect("Failed to run build");
assert!(output.status.success(), "Frontend build failed");
}
}
// Create minimal tauri.conf.json
{
"package": { "productName": "MyApp", "version": "0.1.0" },
"tauri": {
"bundle": {
"identifier": "com.company.myapp",
"icon": ["icons/icon.ico", "icons/icon.icns", "icons/icon.png"]
}
}
}
Add platform-specific tests as you expand configuration:
#[test]
fn test_windows_signing_config() {
let config: serde_json::Value = serde_json::from_str(
&std::fs::read_to_string("src-tauri/tauri.conf.json").unwrap()
).unwrap();
let windows = &config["tauri"]["bundle"]["windows"];
assert!(windows["timestampUrl"].as_str().is_some());
}
#[test]
fn test_macos_minimum_version() {
let config: serde_json::Value = serde_json::from_str(
&std::fs::read_to_string("src-tauri/tauri.conf.json").unwrap()
).unwrap();
let min_ver = config["tauri"]["bundle"]["macOS"]["minimumSystemVersion"]
.as_str().unwrap();
assert!(min_ver >= "10.15", "Must support macOS 10.15+");
}
# Run all build tests
cargo test --manifest-path src-tauri/Cargo.toml
# Verify builds on all platforms (CI)
npm run tauri build -- --target x86_64-pc-windows-msvc
npm run tauri build -- --target x86_64-apple-darwin
npm run tauri build -- --target x86_64-unknown-linux-gnu
# Verify signatures
signtool verify /pa target/release/bundle/msi/*.msi
codesign --verify --deep target/release/bundle/macos/*.app
# Cargo.toml - Enable incremental compilation
[profile.dev]
incremental = true
[profile.release]
incremental = true
lto = "thin" # Faster than "fat" LTO
Good : Incremental builds reuse compiled artifacts
# First build: 2-3 minutes
cargo build --release
# Subsequent builds: 10-30 seconds
cargo build --release
Bad : Clean builds every time
cargo clean && cargo build --release # Always slow
Good : Cache Rust dependencies in CI
- name: Cache Cargo
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
Bad : No caching - downloads dependencies every build
- name: Build
run: cargo build --release # Downloads everything
Good : Maximize parallel jobs
# .cargo/config.toml
[build]
jobs = 8 # Match CPU cores
[target.x86_64-unknown-linux-gnu]
rustflags = ["-C", "link-arg=-fuse-ld=mold"] # Fast linker
Bad : Single-threaded compilation
cargo build -j 1 # Extremely slow
Good : Enable LTO for smaller binaries
[profile.release]
lto = true
codegen-units = 1
panic = "abort"
strip = true
Bad : Debug symbols in release
[profile.release]
debug = true # Bloats binary size
Good : Lazy load routes
// nuxt.config.ts
export default defineNuxtConfig({
experimental: {
treeshakeClientOnly: true
},
vite: {
build: {
rollupOptions: {
output: {
manualChunks: {
'vendor': ['vue', 'pinia'],
'three': ['three', '@tresjs/core']
}
}
}
}
}
})
Bad : Bundle everything together
// Single massive bundle
import * as everything from './all-modules'
Good : Analyze and optimize bundle
# Analyze Rust binary
cargo bloat --release --crates
# Analyze frontend bundle
npx nuxi analyze
Bad : Ignore bundle size
npm run build # Never check what's included
// WRONG: Windows-style path
let config = std::fs::read("C:\\Users\\app\\config.json")?;
// WRONG: Unix-style absolute path
let config = std::fs::read("/home/user/.config/app/config.json")?;
// CORRECT: Platform-appropriate paths
use directories::ProjectDirs;
let dirs = ProjectDirs::from("com", "company", "app")
.expect("Failed to get project directories");
let config_path = dirs.config_dir().join("config.json");
let config = std::fs::read(config_path)?;
# WRONG: Missing Linux dependencies
- name: Build
run: npm run tauri build # Fails on Linux!
# CORRECT: Install platform dependencies
- name: Install Dependencies (Linux)
if: matrix.platform == 'ubuntu-22.04'
run: |
sudo apt-get update
sudo apt-get install -y \
libgtk-3-dev \
libwebkit2gtk-4.0-dev \
libappindicator3-dev
# WRONG: Build universal without both targets
- name: Build macOS Universal
run: npm run tauri build -- --target universal-apple-darwin
# Fails if x86_64 or aarch64 not available!
# CORRECT: Build each architecture separately
- name: Build macOS Intel
run: npm run tauri build -- --target x86_64-apple-darwin
- name: Build macOS ARM
run: npm run tauri build -- --target aarch64-apple-darwin
- name: Create Universal Binary
run: |
lipo -create \
target/x86_64-apple-darwin/release/myapp \
target/aarch64-apple-darwin/release/myapp \
-output target/universal/myapp
# WRONG: Sign without notarization
codesign --sign "Developer ID" MyApp.app
# Users get Gatekeeper warnings!
# CORRECT: Sign and notarize
codesign --sign "Developer ID" --options runtime MyApp.app
xcrun notarytool submit MyApp.zip --apple-id "$APPLE_ID" --password "$APP_PASSWORD" --team-id "$TEAM_ID" --wait
xcrun stapler staple MyApp.app
Your goal is to create cross-platform builds that are:
You understand that cross-platform development requires:
Build Reminder : ALWAYS test on each platform before release. ALWAYS sign your releases. ALWAYS verify signatures work correctly. When in doubt, consult references/security-examples.md for signing procedures.
Weekly Installs
–
Repository
GitHub Stars
32
First Seen
–
Security Audits
agent-browser 浏览器自动化工具 - Vercel Labs 命令行网页操作与测试
147,400 周安装