diff --git a/ShadowsocksX-NG2/Agent/main.swift b/ShadowsocksX-NG2/Agent/main.swift index fa9ad441..3af0faf0 100644 --- a/ShadowsocksX-NG2/Agent/main.swift +++ b/ShadowsocksX-NG2/Agent/main.swift @@ -1,7 +1,7 @@ import Foundation // 代理运行时 wrapper(spec #21 D2/D5/D7,issue #27/#28):LaunchAgent -// 常驻进程,读取跨进程契约 `v2/sslocal-active.json`,承载 PAC HTTP endpoint, +// 常驻进程,读取跨进程契约 `sslocal-active.json`,承载 PAC HTTP endpoint, // 并以绝对配置路径启动官方 sslocal、监管二者生命周期。 // // 协议要点: @@ -17,7 +17,7 @@ import Foundation // 清理后干净退出,避免把上游必然拒绝的配置反复交给 sslocal。 // // 测试缝(生产走默认值):`SSXNG_CONTRACT_PATH`、`SSXNG_SSLOCAL_PATH`、 -// `SSXNG_V2_DIR`(pid 文件与收敛日志的替代目录,测试隔离用)。 +// `SSXNG_RUNTIME_DIR`(pid 文件与收敛日志的替代目录,测试隔离用)。 private let environment = ProcessInfo.processInfo.environment @@ -29,20 +29,20 @@ private let sslocalURL: URL = environment["SSXNG_SSLOCAL_PATH"].map { URL(fileURLWithPath: $0) } ?? Bundle.main.bundleURL.appendingPathComponent("Contents/Helpers/sslocal") -private let v2DirectoryOverride: URL? = environment["SSXNG_V2_DIR"].map { +private let runtimeDirectoryOverride: URL? = environment["SSXNG_RUNTIME_DIR"].map { URL(fileURLWithPath: $0, isDirectory: true) } private let pidFileURL: URL = - v2DirectoryOverride?.appendingPathComponent("agent.pid") ?? RuntimePaths.agentPIDFileURL() + runtimeDirectoryOverride?.appendingPathComponent("agent.pid") ?? RuntimePaths.agentPIDFileURL() // 顶层脚本是按源码顺序执行的:队列必须先于下方任何函数调用完成初始化。 -private let signalsQueue = DispatchQueue(label: "com.qiuyuzhou.ShadowsocksX-NG.agent.signals") +private let signalsQueue = DispatchQueue(label: "com.qiuyuzhou.ShadowsocksX-NG2.agent.signals") // wrapper 与 sslocal 的输出收敛到应用支持目录内的 0600 日志;launchd 统一日 // 志不承载代理细节。 redirectStandardStreams( - to: v2DirectoryOverride?.appendingPathComponent("agent.log") ?? RuntimePaths.agentLogURL()) + to: runtimeDirectoryOverride?.appendingPathComponent("agent.log") ?? RuntimePaths.agentLogURL()) FileManager.default.createFile( atPath: pidFileURL.path, diff --git a/ShadowsocksX-NG2/App/DiagnosticsView.swift b/ShadowsocksX-NG2/App/DiagnosticsView.swift index e5f338a1..475b4228 100644 --- a/ShadowsocksX-NG2/App/DiagnosticsView.swift +++ b/ShadowsocksX-NG2/App/DiagnosticsView.swift @@ -194,7 +194,7 @@ struct DiagnosticsView: View { private static func fileFacts() -> [DiagnosticFileFacts] { [ - DiagnosticFileCollector.collect(label: "v2 目录", url: RuntimePaths.v2Directory()), + DiagnosticFileCollector.collect(label: "运行时目录", url: RuntimePaths.runtimeDirectory()), DiagnosticFileCollector.collect( label: "catalog.json", url: CatalogFileStore.defaultFileURL()), DiagnosticFileCollector.collect( diff --git a/ShadowsocksX-NG2/App/LaunchAgentService.swift b/ShadowsocksX-NG2/App/LaunchAgentService.swift index 63fe1ebe..4d2fe690 100644 --- a/ShadowsocksX-NG2/App/LaunchAgentService.swift +++ b/ShadowsocksX-NG2/App/LaunchAgentService.swift @@ -12,7 +12,7 @@ protocol LaunchAgentControlling { /// 生产实现:plist 必须位于 app bundle `Contents/Library/LaunchAgents/`, /// ProgramArguments 为 bundle 相对路径(launchd 按注册 app 的 bundle 解析)。 struct SMAppLaunchAgentService: LaunchAgentControlling { - static let plistName = "com.qiuyuzhou.ShadowsocksX-NG.agent.plist" + static let plistName = "com.qiuyuzhou.ShadowsocksX-NG2.agent.plist" private let service = SMAppService.agent(plistName: Self.plistName) diff --git a/ShadowsocksX-NG2/Domain/ActivationStateFileStore.swift b/ShadowsocksX-NG2/Domain/ActivationStateFileStore.swift index 2842da43..dc076410 100644 --- a/ShadowsocksX-NG2/Domain/ActivationStateFileStore.swift +++ b/ShadowsocksX-NG2/Domain/ActivationStateFileStore.swift @@ -1,7 +1,7 @@ import Foundation /// 活动目标持久化(spec #21 D3/D5):落盘 `~/Library/Application Support/ -/// ShadowsocksX-NG/v2/activation.json`,权限与原子替换基线同 catalog.json。 +/// ShadowsocksX-NG2/activation.json`,权限与原子替换基线同 catalog.json。 /// 只含目标身份,永不落秘密明文。损坏或版本未知按安全侧恢复为「无活动目标」 /// (代理保持停止,由状态机重校验同步);文件系统级错误仍抛出。 struct ActivationStateFileStore { @@ -19,11 +19,11 @@ struct ActivationStateFileStore { let fileURL: URL - /// 默认位置:`~/Library/Application Support/ShadowsocksX-NG/v2/activation.json`。 + /// 默认位置:`~/Library/Application Support/ShadowsocksX-NG2/activation.json`。 static func defaultFileURL() -> URL { let support = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[ 0] - return support.appendingPathComponent("ShadowsocksX-NG/v2/activation.json") + return support.appendingPathComponent("ShadowsocksX-NG2/activation.json") } /// 文件缺失/损坏/版本未知 → `nil`(安全侧:无活动目标)。 diff --git a/ShadowsocksX-NG2/Domain/CatalogFileStore.swift b/ShadowsocksX-NG2/Domain/CatalogFileStore.swift index a47d7310..0a3bd05a 100644 --- a/ShadowsocksX-NG2/Domain/CatalogFileStore.swift +++ b/ShadowsocksX-NG2/Domain/CatalogFileStore.swift @@ -1,7 +1,7 @@ import Foundation /// 配置树磁盘持久化(spec #21 D5):落盘 `~/Library/Application Support/ -/// ShadowsocksX-NG/v2/catalog.json`,`v2/` 权限 0700、文件 0600;写临时文件 +/// ShadowsocksX-NG2/catalog.json`,目录权限 0700、文件 0600;写临时文件 /// (创建即 0600)后原子替换。文件只含结构化目录、凭据引用与订阅元数据 /// (URL 仍是凭据引用),永不落秘密明文。 struct CatalogFileStore { @@ -23,11 +23,11 @@ struct CatalogFileStore { let fileURL: URL - /// 默认位置:`~/Library/Application Support/ShadowsocksX-NG/v2/catalog.json`。 + /// 默认位置:`~/Library/Application Support/ShadowsocksX-NG2/catalog.json`。 static func defaultFileURL() -> URL { let support = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[ 0] - return support.appendingPathComponent("ShadowsocksX-NG/v2/catalog.json") + return support.appendingPathComponent("ShadowsocksX-NG2/catalog.json") } /// 文件缺失 → 全新空文档;存在但损坏/版本未知/结构不一致 → `.corrupt`。 diff --git a/ShadowsocksX-NG2/Domain/CredentialStore.swift b/ShadowsocksX-NG2/Domain/CredentialStore.swift index e1aa06b1..49e3713c 100644 --- a/ShadowsocksX-NG2/Domain/CredentialStore.swift +++ b/ShadowsocksX-NG2/Domain/CredentialStore.swift @@ -24,7 +24,7 @@ enum CredentialStoreError: Error, Equatable { struct KeychainCredentialStore: CredentialStoring { let service: String - init(service: String = "com.qiuyuzhou.ShadowsocksX-NG.credentials") { + init(service: String = "com.qiuyuzhou.ShadowsocksX-NG2.credentials") { self.service = service } diff --git a/ShadowsocksX-NG2/Domain/ListenSettingsFileStore.swift b/ShadowsocksX-NG2/Domain/ListenSettingsFileStore.swift index 4ee8a41a..4a72581f 100644 --- a/ShadowsocksX-NG2/Domain/ListenSettingsFileStore.swift +++ b/ShadowsocksX-NG2/Domain/ListenSettingsFileStore.swift @@ -37,8 +37,8 @@ struct RestoredListenSettings: Equatable { } /// 用户监听设置(端口、HTTP 启用、UDP、监听范围)的磁盘持久化,落盘 -/// `~/Library/Application Support/ShadowsocksX-NG/listen-settings.json`。 -/// 只承载用户显式确认过的配置;与 v2/ 运行时契约文件分离。监听范围只按 +/// `~/Library/Application Support/ShadowsocksX-NG2/listen-settings.json`。 +/// 只承载用户显式确认过的配置;与运行时契约文件分离。监听范围只按 /// 原样保留,有效性由派生文档的读取侧校验兜底。 struct ListenSettingsFileStore: ListenSettingsStoring { private static let jsonEncoder: JSONEncoder = { @@ -52,7 +52,7 @@ struct ListenSettingsFileStore: ListenSettingsStoring { static func defaultFileURL() -> URL { let support = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[ 0] - return support.appendingPathComponent("ShadowsocksX-NG/listen-settings.json") + return support.appendingPathComponent("ShadowsocksX-NG2/listen-settings.json") } init(fileURL: URL = ListenSettingsFileStore.defaultFileURL()) { diff --git a/ShadowsocksX-NG2/Domain/ManagedPluginProviding.swift b/ShadowsocksX-NG2/Domain/ManagedPluginProviding.swift index 97ec1a8c..d93edb8f 100644 --- a/ShadowsocksX-NG2/Domain/ManagedPluginProviding.swift +++ b/ShadowsocksX-NG2/Domain/ManagedPluginProviding.swift @@ -35,7 +35,7 @@ enum ManagedPluginCatalog { projectURL: "https://github.com/shadowsocks/v2ray-plugin", release: "v1.3.2", license: "MIT", - signIdentifier: "com.qiuyuzhou.ShadowsocksX-NG.plugin.v2ray-plugin") + signIdentifier: "com.qiuyuzhou.ShadowsocksX-NG2.plugin.v2ray-plugin") ] static func info(forProgram program: String) -> ManagedPluginInfo? { diff --git a/ShadowsocksX-NG2/Domain/PACServer.swift b/ShadowsocksX-NG2/Domain/PACServer.swift index ce4cdd75..e4c2f65f 100644 --- a/ShadowsocksX-NG2/Domain/PACServer.swift +++ b/ShadowsocksX-NG2/Domain/PACServer.swift @@ -15,7 +15,7 @@ final class PACServer { private let configuration: PACRuntimeDocument private let requestHeaderTimeout: TimeInterval - private let queue = DispatchQueue(label: "com.qiuyuzhou.ShadowsocksX-NG.pac") + private let queue = DispatchQueue(label: "com.qiuyuzhou.ShadowsocksX-NG2.pac") private let lock = NSLock() private var listener: NWListener? /// 只在 queue 上访问;停止时一并取消,避免 listener 已停但旧连接仍可取 PAC。 diff --git a/ShadowsocksX-NG2/Domain/ProxyRuntimePlan.swift b/ShadowsocksX-NG2/Domain/ProxyRuntimePlan.swift index 2847df99..f71eed5e 100644 --- a/ShadowsocksX-NG2/Domain/ProxyRuntimePlan.swift +++ b/ShadowsocksX-NG2/Domain/ProxyRuntimePlan.swift @@ -13,7 +13,7 @@ enum LaunchAgentStatus: Equatable, Sendable { case notFound } -/// wrapper 进程活性:GUI 读 `v2/agent.pid` 并以 `kill(pid, 0)` 判定(D2)。 +/// wrapper 进程活性:GUI 读 `agent.pid` 并以 `kill(pid, 0)` 判定(D2)。 enum WrapperProcessState: Equatable, Sendable { case notRunning case running(pid: Int32) diff --git a/ShadowsocksX-NG2/Domain/ProxySettings.swift b/ShadowsocksX-NG2/Domain/ProxySettings.swift index 1fa29fa6..c81b8b24 100644 --- a/ShadowsocksX-NG2/Domain/ProxySettings.swift +++ b/ShadowsocksX-NG2/Domain/ProxySettings.swift @@ -206,7 +206,7 @@ struct ProxySettingsFileStore: ProxySettingsStoring { static func defaultFileURL() -> URL { let support = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[ 0] - return support.appendingPathComponent("ShadowsocksX-NG/settings.json") + return support.appendingPathComponent("ShadowsocksX-NG2/settings.json") } func load() throws -> ProxySettings { diff --git a/ShadowsocksX-NG2/Domain/RuntimeFileStore.swift b/ShadowsocksX-NG2/Domain/RuntimeFileStore.swift index 6fd72922..fb0020d9 100644 --- a/ShadowsocksX-NG2/Domain/RuntimeFileStore.swift +++ b/ShadowsocksX-NG2/Domain/RuntimeFileStore.swift @@ -1,6 +1,6 @@ import Foundation -/// 运行时文件存取(spec #21 D5,issue #27):`v2/sslocal-active.json` 是 GUI +/// 运行时文件存取(spec #21 D5,issue #27):`sslocal-active.json` 是 GUI /// 写、wrapper 读的跨进程交接契约。写入复用 `AtomicFileWriter` 的物理安全 /// 基线(目录 0700 含自愈、临时文件创建即 0600、写全校验后原子替换);显式 /// 停止后的清理只做普通 unlink,不承诺安全擦除(D5)。 @@ -16,7 +16,7 @@ struct RuntimeFileStore { self.fileURL = fileURL } - /// wrapper pid 文件与契约同目录(v2/):GUI 判活与 SIGUSR1 投递依据 + /// wrapper pid 文件与契约同目录:GUI 判活与 SIGUSR1 投递依据 /// (SMAppService 不暴露运行中 agent 的 pid)。 var pidFileURL: URL { fileURL.deletingLastPathComponent().appendingPathComponent("agent.pid") diff --git a/ShadowsocksX-NG2/Domain/RuntimePaths.swift b/ShadowsocksX-NG2/Domain/RuntimePaths.swift index bc7a6952..416ecbb8 100644 --- a/ShadowsocksX-NG2/Domain/RuntimePaths.swift +++ b/ShadowsocksX-NG2/Domain/RuntimePaths.swift @@ -1,34 +1,34 @@ import Foundation /// 跨进程运行时路径契约(spec #21 D5,issue #27):GUI 写、wrapper 读的固定 -/// 位置。目录 `v2/` 0700、文件 0600,由写入方与 wrapper 共同维护。 +/// 位置。目录 0700、文件 0600,由写入方与 wrapper 共同维护。 enum RuntimePaths { - static func v2Directory() -> URL { + static func runtimeDirectory() -> URL { FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0] - .appendingPathComponent("ShadowsocksX-NG/v2", isDirectory: true) + .appendingPathComponent("ShadowsocksX-NG2", isDirectory: true) } /// sslocal 配置契约:激活派生的完整 JSON,GUI 原子写入、wrapper 以绝对路径 /// 交给 `sslocal -c`(也是 SIGUSR1 热重载时上游重读的文件)。 static func runtimeFileURL() -> URL { - v2Directory().appendingPathComponent("sslocal-active.json") + runtimeDirectory().appendingPathComponent("sslocal-active.json") } /// wrapper 进程 pid 文件:SMAppService.Status 不暴露运行中 agent 的 pid, /// wrapper 自写此文件供 GUI 判活(kill(pid, 0))并投递 SIGUSR1。 static func agentPIDFileURL() -> URL { - v2Directory().appendingPathComponent("agent.pid") + runtimeDirectory().appendingPathComponent("agent.pid") } /// wrapper 与 sslocal 的收敛日志(stderr 重定向目标);滚动与诊断导出由 #33 /// 完善。不属于「运行时文件」,显式停止时不删除。 static func agentLogURL() -> URL { - v2Directory().appendingPathComponent("agent.log") + runtimeDirectory().appendingPathComponent("agent.log") } /// Original network-service proxy dictionaries captured before 2.0 writes - /// system proxy settings. It is protected like the other v2 runtime files. + /// system proxy settings. It is protected like the other runtime files. static func systemProxyOwnershipURL() -> URL { - v2Directory().appendingPathComponent("system-proxy-ownership.json") + runtimeDirectory().appendingPathComponent("system-proxy-ownership.json") } } diff --git a/ShadowsocksX-NG2/LaunchAgent/com.qiuyuzhou.ShadowsocksX-NG2.agent.plist b/ShadowsocksX-NG2/LaunchAgent/com.qiuyuzhou.ShadowsocksX-NG2.agent.plist new file mode 100644 index 00000000..746b222b --- /dev/null +++ b/ShadowsocksX-NG2/LaunchAgent/com.qiuyuzhou.ShadowsocksX-NG2.agent.plist @@ -0,0 +1,19 @@ + + + + + KeepAlive + + SuccessfulExit + + + Label + com.qiuyuzhou.ShadowsocksX-NG2.agent + ProgramArguments + + Contents/MacOS/ShadowsocksX-NG2Agent + + ThrottleInterval + 2 + + diff --git a/ShadowsocksX-NG2/README.md b/ShadowsocksX-NG2/README.md index cea799b8..d64287b4 100644 --- a/ShadowsocksX-NG2/README.md +++ b/ShadowsocksX-NG2/README.md @@ -10,7 +10,7 @@ ShadowsocksX-NG 的现代化重写版本。新工程的全部源码与构建配 - `project.yml` — XcodeGen 工程定义,唯一事实来源。`.xcodeproj` 与 `App/Info.plist`、`*.entitlements`、`Tests/Info.plist` 都是生成物(已 gitignore),改工程结构一律改 `project.yml` 后重新生成。 - `App/` — app target 源码(SwiftUI 菜单栏 app,LSUIElement)。 -- `Agent/` — 代理运行时 wrapper(独立可执行文件,装入 `Contents/MacOS/`,由 LaunchAgent 常驻):读 `v2/sslocal-active.json` 契约、承载版本化 PAC HTTP endpoint,并以绝对路径启动和监管 sslocal(SIGTERM 链式停止、SIGUSR1 热重载/结构重启、崩溃时非零退出交 KeepAlive 重放,spec #21 D2/D5/D7)。 +- `Agent/` — 代理运行时 wrapper(独立可执行文件,装入 `Contents/MacOS/`,由 LaunchAgent 常驻):读 `sslocal-active.json` 契约、承载版本化 PAC HTTP endpoint,并以绝对路径启动和监管 sslocal(SIGTERM 链式停止、SIGUSR1 热重载/结构重启、崩溃时非零退出交 KeepAlive 重放,spec #21 D2/D5/D7)。 - `LaunchAgent/` — `SMAppService.agent(plistName:)` 的注册清单,装入 `Contents/Library/LaunchAgents/`;`ProgramArguments` 用 bundle 相对路径,由 launchd 按注册 app 的 bundle 位置解析。 - `Domain/` — 领域核心(配置目录树、凭据引用与持久化,spec #21 D3/D5),与 UI 无关;其中 `RuntimePaths`、`SslocalRuntimeDocument`、`RuntimeLog` 同时编入 wrapper 目标,保证跨进程契约单一来源。 - `Tests/` — 单元测试 target,随 `ShadowsocksX-NG2` scheme 运行。 @@ -29,7 +29,7 @@ ShadowsocksX-NG 的现代化重写版本。新工程的全部源码与构建配 ## 签名与运行形态基线 -- 继承 bundle id `com.qiuyuzhou.ShadowsocksX-NG`;macOS 15+ / arm64。 +- bundle id `com.qiuyuzhou.ShadowsocksX-NG2`,与 Legacy(`com.qiuyuzhou.ShadowsocksX-NG`)区分;运行时数据落 `~/Library/Application Support/ShadowsocksX-NG2/`。macOS 15+ / arm64。 - Hardened Runtime + Developer ID 签名(`DEVELOPMENT_TEAM` / 证书见 `project.yml`);不启用 App Sandbox,entitlements 为空 dict。 - 测试 target 用 Apple Development 证书签名:宿主 app 开启 Hardened Runtime 后 library validation 要求被注入的 xctest 包同 Team 签名。 - Release 配置关闭 `CODE_SIGN_INJECT_BASE_ENTITLEMENTS`,保证发布产物 entitlements 只来自项目文件。 diff --git a/ShadowsocksX-NG2/Tests/ActivationStateFileStoreTests.swift b/ShadowsocksX-NG2/Tests/ActivationStateFileStoreTests.swift index e1d86403..cd1cdc58 100644 --- a/ShadowsocksX-NG2/Tests/ActivationStateFileStoreTests.swift +++ b/ShadowsocksX-NG2/Tests/ActivationStateFileStoreTests.swift @@ -73,7 +73,7 @@ final class ActivationStateFileStoreTests: XCTestCase { func testDefaultFileURLMatchesRuntimeDirectoryContract() { XCTAssertTrue( - ActivationStateFileStore.defaultFileURL().path.hasSuffix("ShadowsocksX-NG/v2/activation.json") + ActivationStateFileStore.defaultFileURL().path.hasSuffix("ShadowsocksX-NG2/activation.json") ) } diff --git a/ShadowsocksX-NG2/Tests/AgentLifecycleTests.swift b/ShadowsocksX-NG2/Tests/AgentLifecycleTests.swift index 210d1f10..a9be2e6c 100644 --- a/ShadowsocksX-NG2/Tests/AgentLifecycleTests.swift +++ b/ShadowsocksX-NG2/Tests/AgentLifecycleTests.swift @@ -63,7 +63,7 @@ final class AgentLifecycleTests: XCTestCase { var environment = ProcessInfo.processInfo.environment environment["SSXNG_CONTRACT_PATH"] = contractURL.path environment["SSXNG_SSLOCAL_PATH"] = stubURL.path - environment["SSXNG_V2_DIR"] = workDir.path + environment["SSXNG_RUNTIME_DIR"] = workDir.path environment["SSLOCAL_STUB_STATE"] = stubStateURL.path environment["SSLOCAL_STUB_BEHAVIOR"] = behavior process.environment = environment diff --git a/ShadowsocksX-NG2/Tests/DiagnosticFileCollectorTests.swift b/ShadowsocksX-NG2/Tests/DiagnosticFileCollectorTests.swift index 545d5b5d..1b0b2e3d 100644 --- a/ShadowsocksX-NG2/Tests/DiagnosticFileCollectorTests.swift +++ b/ShadowsocksX-NG2/Tests/DiagnosticFileCollectorTests.swift @@ -11,7 +11,7 @@ final class DiagnosticFileCollectorTests: XCTestCase { directory = FileManager.default.temporaryDirectory .appendingPathComponent("ssxng-diag-\(UUID().uuidString)", isDirectory: true) try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true) - // 临时目录默认带 0755:对齐产品基线(v2/ 0700)再断言。 + // 临时目录默认带 0755:对齐产品基线(运行时目录 0700)再断言。 try FileManager.default.setAttributes( [.posixPermissions: 0o700], ofItemAtPath: directory.path) } @@ -38,7 +38,7 @@ final class DiagnosticFileCollectorTests: XCTestCase { } func testCollectsDirectoryPermissionsWithoutSize() throws { - let facts = DiagnosticFileCollector.collect(label: "v2 目录", url: directory) + let facts = DiagnosticFileCollector.collect(label: "运行时目录", url: directory) XCTAssertTrue(facts.exists) XCTAssertTrue(facts.isDirectory) diff --git a/ShadowsocksX-NG2/Tests/DiagnosticReportTests.swift b/ShadowsocksX-NG2/Tests/DiagnosticReportTests.swift index 0998e719..c49c1536 100644 --- a/ShadowsocksX-NG2/Tests/DiagnosticReportTests.swift +++ b/ShadowsocksX-NG2/Tests/DiagnosticReportTests.swift @@ -167,7 +167,7 @@ final class DiagnosticReportTests: XCTestCase { var snapshot = DiagnosticSnapshot() snapshot.eventLines = [ RuntimeLogEvent.runtimePersistFailed( - detail: "/Users/SECRETUSER/Library/Application Support/ShadowsocksX-NG/v2: errno 13" + detail: "/Users/SECRETUSER/Library/Application Support/ShadowsocksX-NG2: errno 13" ).description ] snapshot.homePathForRedaction = "/Users/SECRETUSER" diff --git a/ShadowsocksX-NG2/Tests/ExternalBinaryManifestTests.swift b/ShadowsocksX-NG2/Tests/ExternalBinaryManifestTests.swift index 1699c516..bd99a301 100644 --- a/ShadowsocksX-NG2/Tests/ExternalBinaryManifestTests.swift +++ b/ShadowsocksX-NG2/Tests/ExternalBinaryManifestTests.swift @@ -16,7 +16,7 @@ final class ExternalBinaryManifestTests: XCTestCase { var signIdentifier: String } - private static let bundleID = "com.qiuyuzhou.ShadowsocksX-NG" + private static let bundleID = "com.qiuyuzhou.ShadowsocksX-NG2" private static let vendorRoot = URL(fileURLWithPath: #filePath) .deletingLastPathComponent() // Tests/ .deletingLastPathComponent() // ShadowsocksX-NG2/ diff --git a/ShadowsocksX-NG2/Tests/ProxyRuntimeControllerTests.swift b/ShadowsocksX-NG2/Tests/ProxyRuntimeControllerTests.swift index c52a8dee..4054f1e5 100644 --- a/ShadowsocksX-NG2/Tests/ProxyRuntimeControllerTests.swift +++ b/ShadowsocksX-NG2/Tests/ProxyRuntimeControllerTests.swift @@ -7,7 +7,7 @@ import XCTest /// 代理开关、目录重展开消费与 GUI 重同步重合(不触真实 SMAppService/launchd)。 @MainActor final class ProxyRuntimeControllerTests: XCTestCase { - private var runtime: ProxyRuntimeFixture.TemporaryV2! + private var runtime: ProxyRuntimeFixture.TemporaryRuntime! private var catalogFileURL: URL! private var activationFileURL: URL! private var credentials: InMemoryCredentialStore! @@ -40,7 +40,7 @@ final class ProxyRuntimeControllerTests: XCTestCase { override func setUpWithError() throws { try super.setUpWithError() - runtime = ProxyRuntimeFixture.makeTemporaryV2() + runtime = ProxyRuntimeFixture.makeTemporaryRuntime() catalogFileURL = runtime.directory.appendingPathComponent("catalog.json") activationFileURL = runtime.directory.appendingPathComponent("activation.json") credentials = InMemoryCredentialStore() diff --git a/ShadowsocksX-NG2/Tests/ProxyRuntimeFixture.swift b/ShadowsocksX-NG2/Tests/ProxyRuntimeFixture.swift index 4ac8d99e..de035072 100644 --- a/ShadowsocksX-NG2/Tests/ProxyRuntimeFixture.swift +++ b/ShadowsocksX-NG2/Tests/ProxyRuntimeFixture.swift @@ -3,21 +3,23 @@ import XCTest @testable import ShadowsocksX_NG2 -/// 代理运行时测试共享夹具(票 #27):临时 v2 目录、契约文档构造、LaunchAgent +/// 代理运行时测试共享夹具(票 #27):临时运行时目录、契约文档构造、LaunchAgent /// 与探测替身。 enum ProxyRuntimeFixture { /// 在临时目录中建一套隔离的运行时文件组(不触碰真实 ~/Library)。 - struct TemporaryV2 { + struct TemporaryRuntime { let directory: URL let contract: URL let pidFile: URL } - static func makeTemporaryV2(file: StaticString = #filePath, line: UInt = #line) -> TemporaryV2 { + static func makeTemporaryRuntime(file: StaticString = #filePath, line: UInt = #line) + -> TemporaryRuntime + { let directory = FileManager.default.temporaryDirectory .appendingPathComponent("ssxng-tests-\(UUID().uuidString)", isDirectory: true) try? FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true) - return TemporaryV2( + return TemporaryRuntime( directory: directory, contract: directory.appendingPathComponent("sslocal-active.json"), pidFile: directory.appendingPathComponent("agent.pid")) diff --git a/ShadowsocksX-NG2/Tests/RealSslocalSmokeTests.swift b/ShadowsocksX-NG2/Tests/RealSslocalSmokeTests.swift index 65e9ff07..d3716ae1 100644 --- a/ShadowsocksX-NG2/Tests/RealSslocalSmokeTests.swift +++ b/ShadowsocksX-NG2/Tests/RealSslocalSmokeTests.swift @@ -72,7 +72,7 @@ final class RealSslocalSmokeTests: XCTestCase { var environment = ProcessInfo.processInfo.environment environment["SSXNG_CONTRACT_PATH"] = contractURL.path environment["SSXNG_SSLOCAL_PATH"] = try sslocalURL.path - environment["SSXNG_V2_DIR"] = workDir.path + environment["SSXNG_RUNTIME_DIR"] = workDir.path process.environment = environment try process.run() return process diff --git a/ShadowsocksX-NG2/Tests/RuntimeFileStoreTests.swift b/ShadowsocksX-NG2/Tests/RuntimeFileStoreTests.swift index 770b976d..ce4fa310 100644 --- a/ShadowsocksX-NG2/Tests/RuntimeFileStoreTests.swift +++ b/ShadowsocksX-NG2/Tests/RuntimeFileStoreTests.swift @@ -5,12 +5,12 @@ import XCTest /// 运行时文件存取(spec #21 D5,issue #27):0700/0600 权限基线、原子替换、 /// 读取侧判定与显式停止清理。 final class RuntimeFileStoreTests: XCTestCase { - private var runtime: ProxyRuntimeFixture.TemporaryV2! + private var runtime: ProxyRuntimeFixture.TemporaryRuntime! private var store: RuntimeFileStore! override func setUpWithError() throws { try super.setUpWithError() - runtime = ProxyRuntimeFixture.makeTemporaryV2() + runtime = ProxyRuntimeFixture.makeTemporaryRuntime() store = RuntimeFileStore(fileURL: runtime.contract) } diff --git a/ShadowsocksX-NG2/Tests/SkeletonTests.swift b/ShadowsocksX-NG2/Tests/SkeletonTests.swift index 3498bdde..2f4859e9 100644 --- a/ShadowsocksX-NG2/Tests/SkeletonTests.swift +++ b/ShadowsocksX-NG2/Tests/SkeletonTests.swift @@ -11,8 +11,9 @@ final class SkeletonTests: XCTestCase { XCTAssertFalse(delegate.applicationShouldTerminateAfterLastWindowClosed(.shared)) } - func testAppBundleIDInheritedFromLegacy() { - XCTAssertEqual(Bundle.main.bundleIdentifier, "com.qiuyuzhou.ShadowsocksX-NG") + func testAppBundleIDIsDistinctFromLegacy() { + XCTAssertEqual(Bundle.main.bundleIdentifier, "com.qiuyuzhou.ShadowsocksX-NG2") + XCTAssertNotEqual(Bundle.main.bundleIdentifier, "com.qiuyuzhou.ShadowsocksX-NG") } func testAppIsMenuBarAgent() { diff --git a/ShadowsocksX-NG2/Tests/UnreadableListenSettingsTests.swift b/ShadowsocksX-NG2/Tests/UnreadableListenSettingsTests.swift index 9b8eff46..855631c4 100644 --- a/ShadowsocksX-NG2/Tests/UnreadableListenSettingsTests.swift +++ b/ShadowsocksX-NG2/Tests/UnreadableListenSettingsTests.swift @@ -6,7 +6,7 @@ import XCTest /// issue #30):占位出厂端口不得部署——停止运行时并点名呈现启动失败。 @MainActor final class UnreadableListenSettingsTests: XCTestCase { - private var runtime: ProxyRuntimeFixture.TemporaryV2! + private var runtime: ProxyRuntimeFixture.TemporaryRuntime! private var catalogFileURL: URL! private var activationFileURL: URL! private var credentials: InMemoryCredentialStore! @@ -15,7 +15,7 @@ final class UnreadableListenSettingsTests: XCTestCase { override func setUpWithError() throws { try super.setUpWithError() - runtime = ProxyRuntimeFixture.makeTemporaryV2() + runtime = ProxyRuntimeFixture.makeTemporaryRuntime() catalogFileURL = runtime.directory.appendingPathComponent("catalog.json") activationFileURL = runtime.directory.appendingPathComponent("activation.json") credentials = InMemoryCredentialStore() diff --git a/ShadowsocksX-NG2/Vendor/sslocal/manifest.json b/ShadowsocksX-NG2/Vendor/sslocal/manifest.json index cc95f5e3..8cdd182d 100644 --- a/ShadowsocksX-NG2/Vendor/sslocal/manifest.json +++ b/ShadowsocksX-NG2/Vendor/sslocal/manifest.json @@ -8,5 +8,5 @@ "archiveMember": "sslocal", "binary": "sslocal", "bundleSubpath": "Helpers", - "signIdentifier": "com.qiuyuzhou.ShadowsocksX-NG.sslocal" + "signIdentifier": "com.qiuyuzhou.ShadowsocksX-NG2.sslocal" } diff --git a/ShadowsocksX-NG2/Vendor/v2ray-plugin/manifest.json b/ShadowsocksX-NG2/Vendor/v2ray-plugin/manifest.json index 015c48f9..d51f4a4f 100644 --- a/ShadowsocksX-NG2/Vendor/v2ray-plugin/manifest.json +++ b/ShadowsocksX-NG2/Vendor/v2ray-plugin/manifest.json @@ -8,5 +8,5 @@ "archiveMember": "v2ray-plugin_darwin_arm64", "binary": "v2ray-plugin", "bundleSubpath": "Helpers/Plugins", - "signIdentifier": "com.qiuyuzhou.ShadowsocksX-NG.plugin.v2ray-plugin" + "signIdentifier": "com.qiuyuzhou.ShadowsocksX-NG2.plugin.v2ray-plugin" } diff --git a/ShadowsocksX-NG2/project.yml b/ShadowsocksX-NG2/project.yml index 43e9b5d0..03a93d11 100644 --- a/ShadowsocksX-NG2/project.yml +++ b/ShadowsocksX-NG2/project.yml @@ -32,7 +32,7 @@ targets: # LaunchAgent 注册清单:SMAppService.agent(plistName:) 从 bundle 内 # Contents/Library/LaunchAgents/ 读取;ProgramArguments 用 bundle 相对路径 # (spec #21 D2,issue #27)。 - - path: LaunchAgent/com.qiuyuzhou.ShadowsocksX-NG.agent.plist + - path: LaunchAgent/com.qiuyuzhou.ShadowsocksX-NG2.agent.plist buildPhase: copyFiles: destination: wrapper @@ -67,7 +67,7 @@ targets: "$SRCROOT/Scripts/sign-embedded-helpers.sh" settings: base: - PRODUCT_BUNDLE_IDENTIFIER: com.qiuyuzhou.ShadowsocksX-NG + PRODUCT_BUNDLE_IDENTIFIER: com.qiuyuzhou.ShadowsocksX-NG2 PRODUCT_NAME: ShadowsocksX-NG2 CODE_SIGN_IDENTITY: "Developer ID Application" ASSETCATALOG_COMPILER_APPICON_NAME: AppIcon @@ -114,7 +114,7 @@ targets: settings: base: PRODUCT_NAME: ShadowsocksX-NG2Agent - PRODUCT_BUNDLE_IDENTIFIER: com.qiuyuzhou.ShadowsocksX-NG.agent + PRODUCT_BUNDLE_IDENTIFIER: com.qiuyuzhou.ShadowsocksX-NG2.agent CODE_SIGN_IDENTITY: "Developer ID Application" ShadowsocksX-NG2Tests: type: bundle.unit-test @@ -123,7 +123,7 @@ targets: - path: Tests settings: base: - PRODUCT_BUNDLE_IDENTIFIER: com.qiuyuzhou.ShadowsocksX-NG.Tests + PRODUCT_BUNDLE_IDENTIFIER: com.qiuyuzhou.ShadowsocksX-NG2.Tests # 必须与宿主 app 同 Team 签名:Hardened Runtime 的 library validation 拒绝 # 加载不同 Team ID 的非平台库,ad-hoc("-")签名的 xctest 包无法注入。 CODE_SIGN_IDENTITY: "Apple Development"