Skip to content

Commit 7bb4250

Browse files
committed
- 新增:开机自动重签名功能。每次用户登录时自动对签名已失效的已迁移应用执行 Ad-hoc 重签名,无需手动操作。默认开启,可在设置中关闭
- 改进:Stub Portal 改用原生 Mach-O 二进制启动器替代旧版 bash 脚本,修复了通过 Finder 双击关联文档无法打开外部应用的问题(#42) - 改进:关于页布局优化,内容区域改为可滚动布局,解决窗口尺寸不足时内容被截断的问题 - 修复:原生 Stub Portal 被误识别为普通本地应用的问题 - 修复:迁回本地时无法正确清理原生 Stub Portal 的问题 - 修复:链接回本地时应用壳被当作完整应用处理的逻辑漏洞 - 修复:AutoResignInstaller 安装失败时静默成功的问题
1 parent e44866a commit 7bb4250

38 files changed

Lines changed: 26566 additions & 385 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,4 @@ docs/superpowers/specs/2026-05-10-logging-improvements-design.md
3737
.kilo/plans/1778362405740-mighty-cactus.md
3838
.kilo/plans/1778449101739-gentle-engine.md
3939
.kilo/
40+
Tools/StubLauncher/launcher

AppPorts.xcodeproj/project.pbxproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@
376376
"@executable_path/../Frameworks",
377377
);
378378
MACOSX_DEPLOYMENT_TARGET = 12.0;
379-
MARKETING_VERSION = 1.6.1;
379+
MARKETING_VERSION = 1.6.2;
380380
PRODUCT_BUNDLE_IDENTIFIER = com.shimoko.AppPorts;
381381
PRODUCT_NAME = "$(TARGET_NAME)";
382382
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -413,7 +413,7 @@
413413
"@executable_path/../Frameworks",
414414
);
415415
MACOSX_DEPLOYMENT_TARGET = 12.0;
416-
MARKETING_VERSION = 1.6.1;
416+
MARKETING_VERSION = 1.6.2;
417417
PRODUCT_BUNDLE_IDENTIFIER = com.shimoko.AppPorts;
418418
PRODUCT_NAME = "$(TARGET_NAME)";
419419
PROVISIONING_PROFILE_SPECIFIER = "";

AppPorts/AboutView.swift

Lines changed: 63 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -178,97 +178,96 @@ struct AboutView: View {
178178
@StateObject private var contributorsViewModel = ContributorsViewModel()
179179

180180
var body: some View {
181-
VStack(spacing: 20) {
182-
181+
VStack(spacing: 0) {
183182
// 1. LOGO 区域
184183
Image(nsImage: NSApplication.shared.applicationIconImage)
185184
.resizable()
186185
.aspectRatio(contentMode: .fit)
187186
.frame(width: 104, height: 104)
188187
.shadow(color: .black.opacity(0.15), radius: 10, x: 0, y: 5)
189-
.padding(.top, 32)
190-
.padding(.bottom, 10)
191-
188+
.padding(.top, 32)
189+
.padding(.bottom, 10)
190+
192191
// 2. 文字信息
193192
VStack(spacing: 6) {
194193
Text("AppPorts".localized)
195194
.font(.system(size: 22, weight: .bold))
196195
.fontWeight(.bold)
197-
196+
198197
Text(String(format: "Version %@".localized, Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "1.0"))
199198
.font(.subheadline)
200199
.foregroundColor(.secondary)
201200
}
202-
.padding(.bottom, 4)
203-
204-
// 3. 描述文案
205-
Text("感谢你使用本工具,外置硬盘拯救世界!".localized)
206-
.font(.body)
207-
.multilineTextAlignment(.center)
208-
.foregroundColor(.primary.opacity(0.9))
209-
.padding(.horizontal, 12)
210-
211-
// 4. 贡献者区域
212-
VStack(alignment: .leading, spacing: 12) {
213-
Text("项目贡献者".localized)
214-
.font(.caption)
215-
.foregroundColor(.secondary)
216-
.frame(maxWidth: .infinity, alignment: .leading)
217-
218-
ScrollView {
219-
LazyVStack(spacing: 10) {
220-
ForEach(contributorsViewModel.contributors) { contributor in
221-
ContributorButton(contributor: contributor)
201+
.padding(.bottom, 8)
202+
203+
// 3. 可滚动内容区域
204+
ScrollView {
205+
VStack(spacing: 20) {
206+
// 描述文案
207+
Text("感谢你使用本工具,外置硬盘拯救世界!".localized)
208+
.font(.body)
209+
.multilineTextAlignment(.center)
210+
.foregroundColor(.primary.opacity(0.9))
211+
.padding(.horizontal, 12)
212+
213+
// 4. 贡献者区域
214+
VStack(alignment: .leading, spacing: 12) {
215+
Text("项目贡献者".localized)
216+
.font(.caption)
217+
.foregroundColor(.secondary)
218+
.frame(maxWidth: .infinity, alignment: .leading)
219+
220+
LazyVStack(spacing: 10) {
221+
ForEach(contributorsViewModel.contributors) { contributor in
222+
ContributorButton(contributor: contributor)
223+
}
222224
}
225+
.frame(maxWidth: .infinity)
226+
}
227+
.padding(16)
228+
.background(
229+
RoundedRectangle(cornerRadius: 18)
230+
.fill(Color.primary.opacity(0.04))
231+
)
232+
.padding(.horizontal, 24)
233+
234+
// 5. 官方链接
235+
VStack(spacing: 10) {
236+
LinkButton(
237+
title: "官方网站".localized,
238+
icon: "globe",
239+
url: "https://appports.shimoko.com/"
240+
)
241+
242+
LinkButton(
243+
title: "用户文档".localized,
244+
icon: "book.fill",
245+
url: "https://docs-appports.shimoko.com/"
246+
)
247+
248+
LinkButton(
249+
title: "项目地址".localized,
250+
icon: "terminal.fill",
251+
url: "https://github.com/wzh4869/AppPorts"
252+
)
223253
}
224-
.frame(maxWidth: .infinity)
254+
.padding(.horizontal, 24)
225255
}
226-
.frame(minHeight: 150, maxHeight: 220)
256+
.padding(.top, 8)
257+
.padding(.bottom, 16)
227258
}
228-
.padding(16)
229-
.background(
230-
RoundedRectangle(cornerRadius: 18)
231-
.fill(Color.primary.opacity(0.04))
232-
)
233-
.padding(.horizontal, 24)
234-
.padding(.top, 4)
235-
236-
// 5. 官方链接
237-
VStack(spacing: 10) {
238-
LinkButton(
239-
title: "官方网站".localized,
240-
icon: "globe",
241-
url: "https://appports.shimoko.com/"
242-
)
243-
244-
LinkButton(
245-
title: "用户文档".localized,
246-
icon: "book.fill",
247-
url: "https://docs-appports.shimoko.com/"
248-
)
249259

250-
LinkButton(
251-
title: "项目地址".localized,
252-
icon: "terminal.fill",
253-
url: "https://github.com/wzh4869/AppPorts"
254-
)
255-
}
256-
.padding(.horizontal, 24)
257-
258-
Spacer()
259-
260-
// 6. 关闭按钮
260+
// 6. 关闭按钮(固定在底部)
261+
Divider()
261262
Button("关闭".localized) {
262263
dismiss()
263264
}
264265
.buttonStyle(.borderedProminent)
265266
.controlSize(.regular)
266267
.keyboardShortcut(.defaultAction)
267-
.padding(.bottom, 30)
268-
268+
.padding(.vertical, 16)
269269
}
270-
.padding(30)
271-
.frame(width: 440, height: 660)
270+
.frame(width: 440, height: 640)
272271
.task {
273272
contributorsViewModel.loadIfNeeded()
274273
}

AppPorts/AppPorts-ReSign.sh

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
#!/bin/bash
2+
# AppPorts Auto Re-Sign LaunchAgent
3+
# Runs at user login to re-sign migrated apps whose ad-hoc signatures
4+
# may have been invalidated by macOS Gatekeeper after restart.
5+
#
6+
# Installed by AppPorts → ~/Library/Application Support/AppPorts/re-sign-at-login.sh
7+
# Triggered by → ~/Library/LaunchAgents/com.shimoko.AppPorts.re-sign.plist
8+
9+
set -euo pipefail
10+
11+
BACKUP_DIR="$HOME/Library/Application Support/AppPorts/signature-backups"
12+
LOG_DIR="$HOME/Library/Application Support/AppPorts"
13+
14+
# 读取 AppPorts 设置中的自定义日志路径,无则用默认路径
15+
CUSTOM_LOG_PATH=$(defaults read com.shimoko.AppPorts LogFilePath 2>/dev/null || true)
16+
if [ -n "$CUSTOM_LOG_PATH" ] && [ -d "$(dirname "$CUSTOM_LOG_PATH")" ]; then
17+
LOG_FILE="$CUSTOM_LOG_PATH"
18+
else
19+
LOG_FILE="$LOG_DIR/AppPorts_Log.txt"
20+
fi
21+
22+
mkdir -p "$LOG_DIR"
23+
24+
log() {
25+
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [RE-SIGN] $1" >> "$LOG_FILE"
26+
}
27+
28+
log "=== 开机重签名任务开始 ==="
29+
30+
if [ ! -d "$BACKUP_DIR" ]; then
31+
log "签名备份目录不存在,跳过: $BACKUP_DIR"
32+
exit 0
33+
fi
34+
35+
shopt -s nullglob
36+
backup_files=("$BACKUP_DIR"/*.plist)
37+
shopt -u nullglob
38+
39+
if [ ${#backup_files[@]} -eq 0 ]; then
40+
log "无签名备份文件,跳过"
41+
exit 0
42+
fi
43+
44+
log "发现 ${#backup_files[@]} 个签名备份文件"
45+
46+
signed_count=0
47+
skipped_count=0
48+
failed_count=0
49+
50+
for plist in "${backup_files[@]}"; do
51+
# 跳过 stub portal 备份(bundleIdentifier 含 .appports.stub)
52+
bundle_id=$(/usr/libexec/PlistBuddy -c "Print :bundleIdentifier" "$plist" 2>/dev/null || true)
53+
if [[ "$bundle_id" == *.appports.stub ]]; then
54+
log "SKIP stub: $(basename "$plist")"
55+
((skipped_count++)) || true
56+
continue
57+
fi
58+
59+
# 跳过非 ad-hoc 签名备份(有开发者证书的不需要重签)
60+
signing_id=$(/usr/libexec/PlistBuddy -c "Print :signingIdentity" "$plist" 2>/dev/null || true)
61+
if [ "$signing_id" != "ad-hoc" ] && [ -n "$signing_id" ]; then
62+
log "SKIP 非 ad-hoc: $(basename "$plist") ($signing_id)"
63+
((skipped_count++)) || true
64+
continue
65+
fi
66+
67+
app_path=$(/usr/libexec/PlistBuddy -c "Print :originalPath" "$plist" 2>/dev/null || true)
68+
if [ -z "$app_path" ] || [ ! -d "$app_path" ]; then
69+
log "SKIP 路径不存在: $(basename "$plist")$app_path"
70+
((skipped_count++)) || true
71+
continue
72+
fi
73+
74+
# 检查是否可写(root 所有的跳过)
75+
if [ ! -w "$app_path" ]; then
76+
log "SKIP 不可写: $app_path"
77+
((skipped_count++)) || true
78+
continue
79+
fi
80+
81+
# 清理隔离属性
82+
/usr/bin/xattr -cr "$app_path" 2>/dev/null || true
83+
84+
# 清理 bundle 根目录杂物
85+
for stray in .DS_Store __MACOSX .git .svn; do
86+
if [ -e "$app_path/$stray" ]; then
87+
rm -rf "$app_path/$stray" 2>/dev/null || true
88+
fi
89+
done
90+
91+
# Ad-hoc 重签名
92+
if /usr/bin/codesign --force --deep --sign - "$app_path" 2>>"$LOG_FILE"; then
93+
log "OK 签名成功: $app_path"
94+
((signed_count++)) || true
95+
else
96+
# 回退无 --deep 的浅层签名
97+
if /usr/bin/codesign --force --sign - "$app_path" 2>>"$LOG_FILE"; then
98+
log "OK 浅层签名: $app_path"
99+
((signed_count++)) || true
100+
else
101+
log "FAIL 签名失败: $app_path"
102+
((failed_count++)) || true
103+
fi
104+
fi
105+
done
106+
107+
log "=== 完成: 成功=$signed_count 跳过=$skipped_count 失败=$failed_count ==="

0 commit comments

Comments
 (0)