diff --git a/.gitignore b/.gitignore index f3828c4..32485c3 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,5 @@ reference-binaries/ !releases/1.2.0/*.pkg !releases/1.3.1/*.pkg + +!releases/1.3.2/*.pkg diff --git a/README.md b/README.md index 531f0f2..8f43cfb 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# 大鱼人像美化 · beatygril +# 大鱼人像美化 · FCP-BeautyGirl 基于本仓库的 FxPlug4 初始工程制作的人像美化滤镜,支持 Motion 和 Final Cut Pro。源码使用 Objective-C、Vision、Core Image 与 Metal。 @@ -6,15 +6,17 @@ 功能包括磨皮、肤色、细节、牙齿美白、眼睛提亮、九类脸型调整、自动人脸跟随和多人处理。原版 MSE18V2 的静态分析依据、近似实现与尚未对齐的部分见 [分析说明](docs/analysis/REA-分析与复刻说明.md)。本版尚未完成原版逐像素与复杂运动跟踪对照。 +1.3.2(17)已统一工程、APP、插件类名和应用ID,正式安装为 `FCP-BeautyGirl.app` / `com.dayu.fcpbeautygirl`,嵌套插件ID为 `com.dayu.fcpbeautygirl.fxplug`。旧APP已迁移清理,滤镜UUID、参数编号和归档格式保持兼容。1901项自动回归及身份迁移检查通过;APP和含配套模板的PKG均已签名、公证、粘附票据和验证。更名后的旧工程重开仍由用户实测。 + ## 使用 -正式安装路径为 `/Library/Plug-Ins/FxPlug/beatygril.app`。Motion 滤镜分类为「大鱼」,滤镜名「大鱼人像美化」。Final Cut Pro 在「效果 → 大鱼磨皮瘦脸 → 大鱼人像美化」中使用配套 Motion 效果,模板目录必须同时保留 `.moef` 与 `Drop Zone.tiff`。美化会随当前帧的人脸自动更新,不需要点击分析。界面以磨皮、美白、瘦脸三张卡片呈现,默认跟随主要人脸;多人模式对多脸变形。单项眼睛可暂时停用并保留数值,右侧箭头恢复默认;磨皮半径、饱和度、锐化和整体混合等收在「更多设置」。旧手动分析与调试参数仅保留隐藏兼容,实际输出统一使用实时自动跟随。 +正式安装路径为 `/Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app`。Motion 滤镜分类为「大鱼」,滤镜名「大鱼人像美化」。Final Cut Pro 在「效果 → 大鱼磨皮瘦脸 → 大鱼人像美化」中使用配套 Motion 效果,模板目录必须同时保留 `.moef` 与 `Drop Zone.tiff`。美化会随当前帧的人脸自动更新,不需要点击分析。界面以磨皮、美白、瘦脸三张卡片呈现,默认跟随主要人脸;多人模式对多脸变形。单项眼睛可暂时停用并保留数值,右侧箭头恢复默认;磨皮半径、饱和度、锐化和整体混合等收在「更多设置」。旧手动分析与调试参数仅保留隐藏兼容,实际输出统一使用实时自动跟随。 1.3.0 的磨皮卡片依次提供「磨皮程度、微细锐化、五官保护、非肤色区保护」。两种保护默认 100%;降低五官保护让五官区域参与更多磨皮,降低非肤色区保护则扩大磨皮范围,均不扩大肤色提亮范围。微细锐化在局部支持区域恢复纹理;旧全局磨皮和全局锐化仍保留在更多设置,旧工程的参数与归档继续兼容。迁移证据、实现边界与验证见[新版磨皮参数迁移](docs/analysis/新版磨皮参数迁移.md)。1.3.0 的新增参数已随 1.3.1 一并提供。 1.3.1(16)修复 V 脸影响眼区的问题,改为四组下颌轮廓到外唇的局部采样,并对齐新目标的 0.065 强度映射。1901 项自动检查通过,APP 与本机 PKG 工坊制作的安装包均已签名、公证和验证。正式 APP 已安装;新效果的原生宿主视觉仍由用户实测。原理与边界见 [V 脸下颌区域修复](docs/analysis/V脸下颌区域修复.md)。 -当前 [1.3.1 正式下载](https://github.com/dayumeida/FCP-BeautyGirl/releases/tag/v1.3.1) 与 [大鱼文章](https://dayu.media/15593/) 已更新,安装包包含「大鱼磨皮瘦脸」分类的配套效果及资源,源码快照也已同步。 +当前 [1.3.2 正式下载](https://github.com/dayumeida/FCP-BeautyGirl/releases/tag/v1.3.2) 与 [大鱼文章](https://dayu.media/15593/) 是本版发布入口,安装包包含「大鱼磨皮瘦脸」分类的配套效果及资源,源码快照也已同步。 ## 开发和恢复 @@ -22,9 +24,9 @@ ```sh ./scripts/test.sh -PAINTFCP_HOST_ASAN=1 ./scripts/test-host.sh -PAINTFCP_OSC_ASAN=1 ./scripts/test-osc.sh -xcodebuild -project beatygril/beatygril.xcodeproj -scheme 'Wrapper Application' -configuration Release -derivedDataPath /tmp/paintfcp-build -destination 'generic/platform=macOS' build +FCP_BEAUTYGIRL_HOST_ASAN=1 ./scripts/test-host.sh +FCP_BEAUTYGIRL_OSC_ASAN=1 ./scripts/test-osc.sh +xcodebuild -project FCP-BeautyGirl/FCP-BeautyGirl.xcodeproj -scheme 'FCP-BeautyGirl' -configuration Release -derivedDataPath /tmp/fcp-beautygirl-build -destination 'generic/platform=macOS' build ``` 工程从 `/Library/Developer/SDKs/FxPlug.sdk` 读取头文件与链接信息,从 `/Library/Developer/Frameworks` 嵌入可运行的 Apple 框架。构建默认使用团队 B3W25FX5H5 的 Developer ID Application 身份;正式交付使用 `python3 scripts/deliver.py` 完成公证、粘附和安装;安装前先关闭 Motion 与 FCP,脚本在检测到宿主仍运行时保留原安装版并停止。原生验收完成后继续脚本的清理阶段,清除临时注册与 APP 副本。 diff --git a/RECOVERY.md b/RECOVERY.md index 50b5953..c4fb553 100644 --- a/RECOVERY.md +++ b/RECOVERY.md @@ -1,5 +1,8 @@ # 恢复说明 +当前产品身份自1.3.2(17)统一为FCP-BeautyGirl:工程 `FCP-BeautyGirl/FCP-BeautyGirl.xcodeproj`、scheme `FCP-BeautyGirl`、APP `/Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app`、主ID `com.dayu.fcpbeautygirl`、插件ID `com.dayu.fcpbeautygirl.fxplug`。签名源码 `767bba8ac11e9bf9268a1e02d1a06d3c120c46aa`,APP公证Accepted `559eedf0-048f-4d0e-b013-6a187695f2da`,已安装并迁移清理旧APP;程序与滤镜参数、UUID保持兼容。中央身份定义见product.json,旧名称只用于经过ID、签名团队和滤镜UUID验证的迁移。下文历史版本保留各自当时的名称与路径。 + + - 大鱼备份远程(origin):`https://dayu.media/git/dayu/paintfcp-fxplug.git` - GitHub 公开远程(github):`https://github.com/dayumeida/FCP-BeautyGirl.git` - 本地项目根:`/Users/dayu/Documents/PaintFCP` @@ -130,3 +133,10 @@ GitHub于2026-10-22更正仓库名为 `FCP-BeautyGirl`,远端为 `https://gith GitHub用户名现为 `dayumeida`,账号ID117798680保持一致。当前公开远端 `https://github.com/dayumeida/FCP-BeautyGirl.git` 的恢复检查点 `2faf7de77929cf7707645ebb38095b03d5729647` 已通过匿名独立克隆、241文件字节/执行位和strict/full fsck核验,产品源码与账号更名前一致。两个仓库的全部分支/标签及Release附件均保留;当前文章、本地github远端和仓库目录表已同步。回执见 `docs/verification/github/username-recovery-20261022.json`。 + + +## 1.3.2 统一产品身份 + +发行入口为 `v1.3.2`;正式 APP 源码为 `767bba8ac11e9bf9268a1e02d1a06d3c120c46aa`。新机器按 `product.json` 的工程、scheme 与路径恢复,运行 `python3 scripts/deliver.py --wait-for-hosts --user-tests`;旧产品先验证 Bundle ID、签名团队和滤镜 UUID,再迁移并支持失败回滚。最终包为 `releases/1.3.2/FCP-BeautyGirl-1.3.2.pkg`,SHA-256 `20a88dacf68cc8812f5af86cfe94c1c0614414a68a019eaeac7ca9d078165ae1`,签名、公证 Accepted `4a831775-2317-4372-a4cc-102415657ea3`、票据和 Gatekeeper 均通过。正式 APP 已安装并清理旧身份。1901 项自动回归、8 项 APP 迁移、8 项最终 PKG 身份脚本迁移与模板隔离测试通过;未运行真实 installer,原生旧工程重开仍待用户检查。 + +源码包须从发行标签导出,排除 PKG 二进制并加入 SOURCE-SNAPSHOT.json。发布与独立克隆恢复回执位于 `docs/verification/v1.3.2/`,不包含用户人脸素材、签名私钥或公证凭据。历史标签和旧版本安装包保留,不覆盖。 diff --git a/docs/GitHub同步说明.md b/docs/GitHub同步说明.md index 57a7e9c..e7b48e1 100644 --- a/docs/GitHub同步说明.md +++ b/docs/GitHub同步说明.md @@ -2,7 +2,7 @@ 公开源码:https://github.com/dayumeida/FCP-BeautyGirl -正式安装包:https://github.com/dayumeida/FCP-BeautyGirl/releases/tag/v1.3.1 +正式安装包:https://github.com/dayumeida/FCP-BeautyGirl/releases/tag/v1.3.2 本地 `github` 远端指向上述 GitHub 仓库,`origin` 继续指向大鱼仓库,`main` 仍跟随 `origin/main`。当前项目的完整提交历史、main 和全部现有版本标签已推送,两端引用与本地一致;未改写历史或覆盖标签。后续更新需要分别推送到 origin 和 github,不会自动定时同步。 @@ -23,7 +23,7 @@ git switch --detach 356683b41af702d6c0143ad7fa5f540d752d6e14 源代码编写署名:代码由 OpenAI + Codex 全程编写,站长仅参与调试和测试。 -## 1.3.1 当前发行与恢复 +## 1.3.1 发行与恢复 main已快进纳入1.3.0磨皮参数和1.3.1的V脸下颌修复;origin和github的main及v1.3.1引用已回读核验。v1.3.1固定完整SHA为 `b35b4a5d1ca252d2f83c798613560a768274de3c`,签名APP源码为 `7fa3298e022946e77dfd8cfba1bb5ff2c2a6361f`。独立匿名GitHub克隆232文件字节与执行位一致、strict/full fsck和恢复PKG签名/票据通过。无LFS或子模块。后续main仅补充发布记录,不改写标签。 @@ -40,3 +40,8 @@ GitHub仓库从 `FAMEDaYu/FCP-BeatyGril` 更名为 `FAMEDaYu/FCP-BeautyGirl`, ## 2026-10-22 用户名更改 用户将GitHub登录名从 `FAMEDaYu` 改为 `dayumeida`,账号ID117798680保持不变。两个公开仓库迁移到dayumeida名下:FCP-BeautyGirl(ID1428817365)和FCPX-(ID904215594)。更名前后全部分支/标签SHA及Release/附件ID、名称、大小一致。当前项目github远端为 `https://github.com/dayumeida/FCP-BeautyGirl.git`,origin及main上游保持原大鱼仓库;文章15593、活动文档和Release说明更新为新地址。正式版本标签、公证APP/PKG和源码快照保留原始字节,不为账号更名重新构建。个人主页新地址为 `https://github.com/dayumeida`,旧个人主页不提供重定向;仓库旧地址由GitHub重定向。历史验证回执保留当时用户名。账号更名核验见verification/github/username-20261022.json。 + + +## 1.3.2 产品身份统一 + +发行标签 `v1.3.2`,正式 APP 签名源码 `767bba8ac11e9bf9268a1e02d1a06d3c120c46aa`。新工程和 APP 名为 FCP-BeautyGirl,应用标识及安装路径见 product.json;旧身份仅保留经过验证的迁移入口。GitHub、大鱼 Release 和文章 15593 使用新名称安装包及源码快照,历史发行资料不改写。当前交付与发布核验保存在 verification/v1.3.2。 diff --git a/docs/verification/v1.3.2/core-tests.txt b/docs/verification/v1.3.2/core-tests.txt new file mode 100644 index 0000000..b61547b --- /dev/null +++ b/docs/verification/v1.3.2/core-tests.txt @@ -0,0 +1,5 @@ +Beauty renderer: 57 checks, 0 failures (Metal). +2026-10-22 18:30:09.786 face-tests[35177:8446323] FBBA: creating VNFaceBBoxAligner from VNFaceDetectorRevision2: VNFaceDetectorRevision2 +Face processing: 116 checks passed +2026-10-22 18:30:23.734 slim-tests[35267:8446747] FBBA: creating VNFaceBBoxAligner from VNFaceDetectorRevision2: VNFaceDetectorRevision2 +PASS: 1033 slim alignment checks; maximum UV oracle error 1.09828022e-05 diff --git a/docs/verification/v1.3.2/delivery-receipt.json b/docs/verification/v1.3.2/delivery-receipt.json new file mode 100644 index 0000000..6a9b756 --- /dev/null +++ b/docs/verification/v1.3.2/delivery-receipt.json @@ -0,0 +1,50 @@ +{ + "status": "installed_user_qa_pending", + "bundleIdentifier": "com.dayu.fcpbeautygirl", + "installationPath": "/Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app", + "options": { + "waitForHosts": true, + "userTests": true + }, + "nativeEffectQA": { + "status": "pending_user", + "performedByScript": false + }, + "source": { + "commit": "767bba8ac11e9bf9268a1e02d1a06d3c120c46aa", + "trackedWorktreeChanges": [] + }, + "updatedAt": "2026-10-22T10:38:29.383549+00:00", + "version": "1.3.2", + "build": "17", + "stagingSignature": "Executable=/private/tmp/fcp-beautygirl-release/Build/Products/Release/FCP-BeautyGirl.app/Contents/MacOS/FCP-BeautyGirl\nIdentifier=com.dayu.fcpbeautygirl\nFormat=app bundle with Mach-O universal (x86_64 arm64)\nCodeDirectory v=20500 size=482 flags=0x10000(runtime) hashes=4+7 location=embedded\nHash type=sha256 size=32\nCandidateCDHash sha256=28f0e84981124d854690d9be80e8ecb7a855ed02\nCandidateCDHashFull sha256=28f0e84981124d854690d9be80e8ecb7a855ed02e3de84009119fc616e22aecd\nHash choices=sha256\nCMSDigest=28f0e84981124d854690d9be80e8ecb7a855ed02e3de84009119fc616e22aecd\nCMSDigestType=2\nCDHash=28f0e84981124d854690d9be80e8ecb7a855ed02\nSignature size=8968\nAuthority=Developer ID Application: zhong luo (B3W25FX5H5)\nAuthority=Developer ID Certification Authority\nAuthority=Apple Root CA\nTimestamp=Oct 22, 2026 at 18:38:05\nInfo.plist entries=23\nTeamIdentifier=B3W25FX5H5\nRuntime Version=27.0.0\nSealed Resources version=2 rules=13 files=5\nInternal requirements count=1 size=184\nTotal signatures=1\nChosen signature=1\n", + "notarization": { + "id": "559eedf0-048f-4d0e-b013-6a187695f2da", + "message": "Processing complete", + "status": "Accepted" + }, + "signingVerified": true, + "stapleValidated": true, + "stagingPluginUnregistered": true, + "runningHosts": [], + "installedSignature": "Executable=/Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app/Contents/MacOS/FCP-BeautyGirl\nIdentifier=com.dayu.fcpbeautygirl\nFormat=app bundle with Mach-O universal (x86_64 arm64)\nCodeDirectory v=20500 size=482 flags=0x10000(runtime) hashes=4+7 location=embedded\nHash type=sha256 size=32\nCandidateCDHash sha256=28f0e84981124d854690d9be80e8ecb7a855ed02\nCandidateCDHashFull sha256=28f0e84981124d854690d9be80e8ecb7a855ed02e3de84009119fc616e22aecd\nHash choices=sha256\nCMSDigest=28f0e84981124d854690d9be80e8ecb7a855ed02e3de84009119fc616e22aecd\nCMSDigestType=2\nCDHash=28f0e84981124d854690d9be80e8ecb7a855ed02\nSignature size=8968\nAuthority=Developer ID Application: zhong luo (B3W25FX5H5)\nAuthority=Developer ID Certification Authority\nAuthority=Apple Root CA\nTimestamp=Oct 22, 2026 at 18:38:05\nNotarization Ticket=stapled\nInfo.plist entries=23\nTeamIdentifier=B3W25FX5H5\nRuntime Version=27.0.0\nSealed Resources version=2 rules=13 files=5\nInternal requirements count=1 size=184\nTotal signatures=1\nChosen signature=1\n", + "installedSignatureVerified": true, + "installedStapleValidated": true, + "gatekeeperAccepted": true, + "identityMigration": { + "legacyInstallationsRemoved": [ + "/Library/Plug-Ins/FxPlug/beatygril.app" + ], + "filterUUIDPreserved": "FB14B0E6-59B2-4836-916F-CD0C559E9241" + }, + "pluginDiscovery": { + "bundleIdentifier": "com.dayu.fcpbeautygirl.fxplug", + "formalPath": "/Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app/Contents/PlugIns/FCP-BeautyGirl XPC Service.pluginkit", + "output": " com.dayu.fcpbeautygirl.fxplug(1.1)\t6C990AD2-B58E-4C2F-91A4-927849E7AA10\t2026-10-22 10:38:29 +0000\t/Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app/Contents/PlugIns/FCP-BeautyGirl XPC Service.pluginkit\n (1 plug-in)\n" + }, + "removedGeneratedPaths": [ + "/Users/dayu/Documents/PaintFCP/build/delivery/legacy-beatygril.app", + "/tmp/fcp-beautygirl-release", + "/Users/dayu/Documents/PaintFCP/build/delivery/notarization.zip" + ] +} diff --git a/docs/verification/v1.3.2/host-tests.txt b/docs/verification/v1.3.2/host-tests.txt new file mode 100644 index 0000000..17a9c1c --- /dev/null +++ b/docs/verification/v1.3.2/host-tests.txt @@ -0,0 +1,37 @@ +2026-10-22 18:30:11.816 host-tests[35227:8446515] FBBA: creating VNFaceBBoxAligner from VNFaceDetectorRevision2: VNFaceDetectorRevision2 +Default IOSurface maximum byte difference: 0 +Alternate destination tag maximum byte difference: 0 +Anatomy default 0/0 max=0 mean=0.0000000 eye=0/0 chin=0 inverted=0 +Anatomy default 0/1 max=0 mean=0.0000000 eye=0/0 chin=0 inverted=0 +Anatomy default 1/0 max=0 mean=0.0000000 eye=0/0 chin=0 inverted=0 +Anatomy default 1/1 max=0 mean=0.0000000 eye=0/0 chin=0 inverted=0 +Anatomy big-eye 0/0 max=2 mean=0.0008917 eye=33510/33880 chin=0 inverted=0 +Anatomy big-eye 0/1 max=2 mean=0.0008917 eye=33510/33880 chin=0 inverted=0 +Anatomy big-eye 1/0 max=2 mean=0.0008917 eye=33510/33880 chin=0 inverted=0 +Anatomy big-eye 1/1 max=2 mean=0.0008917 eye=33510/33880 chin=0 inverted=0 +Anatomy chin-positive 0/0 max=1 mean=0.0001459 eye=0/0 chin=116812 inverted=0 +Anatomy chin-positive 0/1 max=1 mean=0.0001459 eye=0/0 chin=116812 inverted=0 +Anatomy chin-positive 1/0 max=1 mean=0.0001459 eye=0/0 chin=116812 inverted=0 +Anatomy chin-positive 1/1 max=1 mean=0.0001459 eye=0/0 chin=116812 inverted=0 +Anatomy chin-negative 0/0 max=1 mean=0.0000849 eye=0/0 chin=60426 inverted=0 +Anatomy chin-negative 0/1 max=1 mean=0.0000849 eye=0/0 chin=60426 inverted=0 +Anatomy chin-negative 1/0 max=1 mean=0.0000849 eye=0/0 chin=60426 inverted=0 +Anatomy chin-negative 1/1 max=1 mean=0.0000849 eye=0/0 chin=60426 inverted=0 +Anatomy short-positive 0/0 max=2 mean=0.0011072 eye=0/0 chin=285953 inverted=0 +Anatomy short-positive 0/1 max=2 mean=0.0011072 eye=0/0 chin=285953 inverted=0 +Anatomy short-positive 1/0 max=2 mean=0.0011072 eye=0/0 chin=285953 inverted=0 +Anatomy short-positive 1/1 max=2 mean=0.0011072 eye=0/0 chin=285953 inverted=0 +Anatomy short-negative 0/0 max=2 mean=0.0008583 eye=0/0 chin=225648 inverted=0 +Anatomy short-negative 0/1 max=2 mean=0.0008583 eye=0/0 chin=225648 inverted=0 +Anatomy short-negative 1/0 max=2 mean=0.0008583 eye=0/0 chin=225648 inverted=0 +Anatomy short-negative 1/1 max=2 mean=0.0008583 eye=0/0 chin=225648 inverted=0 +Anatomy jaw 0/0 max=1 mean=0.0001974 eye=0/0 chin=12951 inverted=0 +Anatomy jaw 0/1 max=1 mean=0.0001974 eye=0/0 chin=12951 inverted=0 +Anatomy jaw 1/0 max=1 mean=0.0001974 eye=0/0 chin=12951 inverted=0 +Anatomy jaw 1/1 max=1 mean=0.0001974 eye=0/0 chin=12951 inverted=0 +Anatomy v-face 0/0 max=1 mean=0.0001240 eye=0/0 chin=39246 inverted=0 +Anatomy v-face 0/1 max=1 mean=0.0001240 eye=0/0 chin=39246 inverted=0 +Anatomy v-face 1/0 max=1 mean=0.0001240 eye=0/0 chin=39246 inverted=0 +Anatomy v-face 1/1 max=1 mean=0.0001240 eye=0/0 chin=39246 inverted=0 +Legacy fallback upright comparison: max=2 mean=0.0010357 +Host adapter: 370 checks, 0 failures diff --git a/docs/verification/v1.3.2/inspector-tests.txt b/docs/verification/v1.3.2/inspector-tests.txt new file mode 100644 index 0000000..aa77638 --- /dev/null +++ b/docs/verification/v1.3.2/inspector-tests.txt @@ -0,0 +1 @@ +Inspector: 227 checks, 0 failures diff --git a/docs/verification/v1.3.2/installed-verification.json b/docs/verification/v1.3.2/installed-verification.json new file mode 100644 index 0000000..3c914b9 --- /dev/null +++ b/docs/verification/v1.3.2/installed-verification.json @@ -0,0 +1,35 @@ +{ + "status": "verified", + "installedProduct": { + "path": "/Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app", + "bundleId": "com.dayu.fcpbeautygirl", + "version": "1.3.2" + }, + "signatureAllArchitecturesVerified": true, + "scopedInventory": { + "roots": [ + "/Applications", + "/Library/Plug-Ins/FxPlug", + "/Users/dayu/Library/Plug-Ins/FxPlug", + "/Users/dayu/Documents/PaintFCP", + "/Users/dayu/Desktop", + "/Users/dayu/Downloads", + "/Users/dayu/Library/Developer/Xcode/DerivedData", + "/tmp/fcp-beautygirl-release", + "/tmp/paintfcp-build-20261021" + ], + "products": [ + { + "path": "/Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app", + "bundleId": "com.dayu.fcpbeautygirl", + "version": "1.3.2" + } + ], + "errors": [], + "scope": "Listed roots only; not all mounted volumes" + }, + "legacyPluginOriginalPathAbsent": true, + "filterUUIDPreserved": "FB14B0E6-59B2-4836-916F-CD0C559E9241", + "templateBytesUnchanged": true, + "nativeOldProjectReopen": "pending_user" +} diff --git a/docs/verification/v1.3.2/installer-tests.txt b/docs/verification/v1.3.2/installer-tests.txt new file mode 100644 index 0000000..43fb2b1 --- /dev/null +++ b/docs/verification/v1.3.2/installer-tests.txt @@ -0,0 +1,5 @@ +........ +---------------------------------------------------------------------- +Ran 8 tests in 2.805s + +OK diff --git a/docs/verification/v1.3.2/migration-tests.txt b/docs/verification/v1.3.2/migration-tests.txt new file mode 100644 index 0000000..c5bb09a --- /dev/null +++ b/docs/verification/v1.3.2/migration-tests.txt @@ -0,0 +1,5 @@ +........ +---------------------------------------------------------------------- +Ran 8 tests in 0.029s + +OK diff --git a/docs/verification/v1.3.2/osc-tests.txt b/docs/verification/v1.3.2/osc-tests.txt new file mode 100644 index 0000000..e85c587 --- /dev/null +++ b/docs/verification/v1.3.2/osc-tests.txt @@ -0,0 +1,21 @@ +2026-10-22 18:30:08.818 osc-tests[35208:8446417] PGFaceOSC point-write desired=(0.25,0.75) setter=(0.25,0.75) readback=(0.25,0.75) mode=normalized verified=1 +2026-10-22 18:30:08.826 osc-tests[35208:8446417] PGFaceOSC coordinates selection=(0.25,0.75) objectBounds=(-100,-50,200,100) canvas=(80,180) surface=320x240 origin=0 +2026-10-22 18:30:09.311 osc-tests[35208:8446417] PGFaceOSC point-write desired=(0.8,0.3) setter=(1152,-432) readback=(0.8,0.3) mode=centered-pixels verified=1 +2026-10-22 18:30:09.311 osc-tests[35208:8446417] PGFaceOSC coordinates selection=(0.7,0.4) objectBounds=(-1920,-1080,3840,2160) canvas=(152,78) surface=320x240 origin=0 +2026-10-22 18:30:09.359 osc-tests[35208:8446417] PGFaceOSC point-write desired=(0.8,0.3) setter=(0.8,0.3) readback=(0.12345,0.87654) mode=normalized verified=0 +2026-10-22 18:30:09.359 osc-tests[35208:8446417] PGFaceOSC point-write desired=(0.75,0.25) setter=(0.75,0.25) readback=(0.75,0.25) mode=normalized verified=1 +2026-10-22 18:30:09.360 osc-tests[35208:8446417] PGFaceOSC coordinates selection=(0.75,0.25) objectBounds=(-100,-50,200,100) canvas=(160,60) surface=320x240 origin=0 +2026-10-22 18:30:09.409 osc-tests[35208:8446417] PGFaceOSC point-write desired=(0.625,0.375) setter=(0.625,0.375) readback=(0.625,0.375) mode=normalized verified=1 +2026-10-22 18:30:09.409 osc-tests[35208:8446417] PGFaceOSC coordinates selection=(0.625,0.375) objectBounds=(-100,-50,200,100) canvas=(240,60) surface=320x240 origin=0 +2026-10-22 18:30:09.457 osc-tests[35208:8446417] PGFaceOSC point-write desired=(0.25,0.75) setter=(0.25,0.75) readback=(0.25,0.75) mode=normalized verified=1 +2026-10-22 18:30:09.457 osc-tests[35208:8446417] PGFaceOSC coordinates selection=(0.25,0.75) objectBounds=(-100,-50,200,100) canvas=(144,180) surface=320x240 origin=0 +2026-10-22 18:30:09.506 osc-tests[35208:8446417] PGFaceOSC point-write desired=(0.5,0.5) setter=(0.5,0.5) readback=(0.5,0.5) mode=normalized verified=1 +2026-10-22 18:30:09.506 osc-tests[35208:8446417] PGFaceOSC coordinates selection=(0,1) objectBounds=(-100,-50,200,100) canvas=(100,140) surface=320x240 origin=0 +2026-10-22 18:30:09.574 osc-tests[35208:8446417] PGFaceOSC coordinates selection=(0.25,0.75) objectBounds=(-100,-50,200,100) canvas=(nan,nan) surface=320x240 origin=0 +2026-10-22 18:30:09.628 osc-tests[35208:8446417] PGFaceOSC point-write desired=(0.5,0.5) setter=(0.5,0.5) readback=(0.5,0.5) mode=normalized verified=1 +2026-10-22 18:30:09.629 osc-tests[35208:8446417] PGFaceOSC coordinates selection=(0.25,0.75) objectBounds=(-100,-50,200,100) canvas=(inf,nan) surface=320x240 origin=0 +2026-10-22 18:30:09.689 osc-tests[35208:8446417] PGFaceOSC point-write desired=(0.5,0.5) setter=(0.5,0.5) readback=(0.5,0.5) mode=normalized verified=1 +2026-10-22 18:30:09.690 osc-tests[35208:8446417] PGFaceOSC coordinates selection=(0.25,0.75) objectBounds=(-100,-50,200,100) canvas=(80,nan) surface=320x240 origin=0 +2026-10-22 18:30:09.742 osc-tests[35208:8446417] PGFaceOSC point-write desired=(0.5,0.5) setter=(0.5,0.5) readback=(0.5,0.5) mode=normalized verified=1 +2026-10-22 18:30:09.742 osc-tests[35208:8446417] PGFaceOSC point-write desired=(0.5,0.5) setter=(0.5,0.5) readback=(0.5,0.5) mode=normalized verified=1 +OSC adapter: 98 checks, 0 failures diff --git a/docs/verification/v1.3.2/pkg-delivery.json b/docs/verification/v1.3.2/pkg-delivery.json new file mode 100644 index 0000000..9287f1d --- /dev/null +++ b/docs/verification/v1.3.2/pkg-delivery.json @@ -0,0 +1,41 @@ +{ + "status": "verified", + "version": "1.3.2", + "build": "17", + "package": "releases/1.3.2/FCP-BeautyGirl-1.3.2.pkg", + "bytes": 4889706, + "sha256": "20a88dacf68cc8812f5af86cfe94c1c0614414a68a019eaeac7ca9d078165ae1", + "sourceApp": "/Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app", + "sourceCommit": "767bba8ac11e9bf9268a1e02d1a06d3c120c46aa", + "motionSource": "/Users/dayu/Movies/Motion Templates.localized/Effects.localized/大鱼磨皮瘦脸", + "motionFiles": [ + { + "file": "大鱼人像美化/Drop Zone.tiff", + "sha256": "a7f8e3a2a431dbb3ce9e777040bb55e701cfe9ab9632e8cbb10359732bc8692e", + "locked": false + }, + { + "file": "大鱼人像美化/large.png", + "sha256": "a02ddb255b1ae255c749bb837a759e2dee1c3b8ffb9bdc1167957761bd5a1b97", + "locked": true + }, + { + "file": "大鱼人像美化/small.png", + "sha256": "1552b838dd88f60dee0795e20aaca652f9c558ecefc93500e895fbff77c2a4d5", + "locked": true + }, + { + "file": "大鱼人像美化/大鱼人像美化.moef", + "sha256": "e9fa97dc14323d0daa7d3d61b06a78ba4000a70ab9f5e67ebffd750d49df1e15", + "locked": false + } + ], + "packager": "/Applications/PKG工坊.app/Contents/MacOS/PKG工坊", + "productMigration": "PKG Forge base plus identity-checked backup/rollback block; final package freshly signed, notarized and stapled", + "notarization": { + "message": "Processing complete", + "id": "4a831775-2317-4372-a4cc-102415657ea3", + "status": "Accepted" + }, + "realInstallerExecuted": false +} diff --git a/docs/verification/v1.3.2/pkg-gatekeeper.txt b/docs/verification/v1.3.2/pkg-gatekeeper.txt new file mode 100644 index 0000000..7b57a35 --- /dev/null +++ b/docs/verification/v1.3.2/pkg-gatekeeper.txt @@ -0,0 +1,3 @@ +/Users/dayu/Documents/PaintFCP/releases/1.3.2/FCP-BeautyGirl-1.3.2.pkg: accepted +source=Notarized Developer ID +origin=Developer ID Installer: zhong luo (B3W25FX5H5) diff --git a/docs/verification/v1.3.2/pkg-notarization.json b/docs/verification/v1.3.2/pkg-notarization.json new file mode 100644 index 0000000..f51a3a5 --- /dev/null +++ b/docs/verification/v1.3.2/pkg-notarization.json @@ -0,0 +1,5 @@ +{ + "message": "Processing complete", + "id": "4a831775-2317-4372-a4cc-102415657ea3", + "status": "Accepted" +} diff --git a/docs/verification/v1.3.2/pkg-signature.txt b/docs/verification/v1.3.2/pkg-signature.txt new file mode 100644 index 0000000..5acd7b0 --- /dev/null +++ b/docs/verification/v1.3.2/pkg-signature.txt @@ -0,0 +1,22 @@ +Package "FCP-BeautyGirl-1.3.2.pkg": + Status: signed by a developer certificate issued by Apple for distribution + Notarization: trusted by the Apple notary service + Signed with a trusted timestamp on: 2026-10-22 10:40:28 +0000 + Certificate Chain: + 1. Developer ID Installer: zhong luo (B3W25FX5H5) + Expires: 2027-02-01 22:12:15 +0000 + SHA256 Fingerprint: + 4D AC CD 04 A5 4C 7E F8 13 13 A2 ED D1 C2 C0 D0 29 EF FA BF 7B 53 + 5A 43 C8 72 4D C4 0A A3 94 34 + ------------------------------------------------------------------------ + 2. Developer ID Certification Authority + Expires: 2027-02-01 22:12:15 +0000 + SHA256 Fingerprint: + 7A FC 9D 01 A6 2F 03 A2 DE 96 37 93 6D 4A FE 68 09 0D 2D E1 8D 03 + F2 9C 88 CF B0 B1 BA 63 58 7F + ------------------------------------------------------------------------ + 3. Apple Root CA + Expires: 2035-02-09 21:40:36 +0000 + SHA256 Fingerprint: + B0 B1 73 0E CB C7 FF 45 05 14 2C 49 F1 29 5E 6E DA 6B CA ED 7E 2C + 68 C5 BE 91 B5 A1 10 01 F0 24 diff --git a/docs/verification/v1.3.2/pkg-signing.txt b/docs/verification/v1.3.2/pkg-signing.txt new file mode 100644 index 0000000..5045b25 --- /dev/null +++ b/docs/verification/v1.3.2/pkg-signing.txt @@ -0,0 +1,5 @@ +productsign: using timestamp authority for signature +productsign: signing product with identity "Developer ID Installer: zhong luo (B3W25FX5H5)" from keychain /Users/dayu/Library/Keychains/login.keychain-db +productsign: adding certificate "Developer ID Certification Authority" +productsign: adding certificate "Apple Root CA" +productsign: Wrote signed product archive to /Users/dayu/Documents/PaintFCP/releases/1.3.2/FCP-BeautyGirl-1.3.2.pkg diff --git a/docs/verification/v1.3.2/pkg-staple.txt b/docs/verification/v1.3.2/pkg-staple.txt new file mode 100644 index 0000000..1a614c4 --- /dev/null +++ b/docs/verification/v1.3.2/pkg-staple.txt @@ -0,0 +1,3 @@ +Processing: /Users/dayu/Documents/PaintFCP/releases/1.3.2/FCP-BeautyGirl-1.3.2.pkg +Processing: /Users/dayu/Documents/PaintFCP/releases/1.3.2/FCP-BeautyGirl-1.3.2.pkg +The staple and validate action worked! diff --git a/docs/verification/v1.3.2/pkg-verification-output.txt b/docs/verification/v1.3.2/pkg-verification-output.txt new file mode 100644 index 0000000..c37ba30 --- /dev/null +++ b/docs/verification/v1.3.2/pkg-verification-output.txt @@ -0,0 +1,36 @@ +{ + "package": "releases/1.3.2/FCP-BeautyGirl-1.3.2.pkg", + "bytes": 4889706, + "sha256": "20a88dacf68cc8812f5af86cfe94c1c0614414a68a019eaeac7ca9d078165ae1", + "version": "1.3.2", + "build": "17", + "app_files_compared": 34, + "app_byte_and_symlink_equality": true, + "template_files": { + "大鱼人像美化.moef": "e9fa97dc14323d0daa7d3d61b06a78ba4000a70ab9f5e67ebffd750d49df1e15", + "Drop Zone.tiff": "a7f8e3a2a431dbb3ce9e777040bb55e701cfe9ab9632e8cbb10359732bc8692e", + "large.png": "a02ddb255b1ae255c749bb837a759e2dee1c3b8ffb9bdc1167957761bd5a1b97", + "small.png": "1552b838dd88f60dee0795e20aaca652f9c558ecefc93500e895fbff77c2a4d5" + }, + "locked_relative_paths": [ + "大鱼人像美化/large.png", + "大鱼人像美化/small.png" + ], + "script_sha256": { + "preinstall": "b0562f7705c84a2f34e31df67a4f0d429e60083bab122dad99f56a4b266c0174", + "postinstall": "d147ccab1c428659fd2b0ed60e77399d1eb5910d1049dcdcbdbfc88fd3824300" + }, + "isolated_script_tests": [ + "fresh install", + "replace old locked thumbnail", + "partial copy failure restores old bytes and locks" + ], + "isolation_changes": [ + "Console identity/home redirected to task fixture", + "chown replaced with true", + "FxPlug verification skipped; formal app signature and discovery verified separately" + ], + "real_installer_execution": false, + "app_and_template_installed": "APP installed through signed delivery workflow; formal template files matched the specified installed category, not changed by running this PKG", + "fonts": "No editable third-party font dependency; promotional lettering baked into PNG. No optional font ZIP needed." +} diff --git a/docs/verification/v1.3.2/pkg-verification.json b/docs/verification/v1.3.2/pkg-verification.json new file mode 100644 index 0000000..c37ba30 --- /dev/null +++ b/docs/verification/v1.3.2/pkg-verification.json @@ -0,0 +1,36 @@ +{ + "package": "releases/1.3.2/FCP-BeautyGirl-1.3.2.pkg", + "bytes": 4889706, + "sha256": "20a88dacf68cc8812f5af86cfe94c1c0614414a68a019eaeac7ca9d078165ae1", + "version": "1.3.2", + "build": "17", + "app_files_compared": 34, + "app_byte_and_symlink_equality": true, + "template_files": { + "大鱼人像美化.moef": "e9fa97dc14323d0daa7d3d61b06a78ba4000a70ab9f5e67ebffd750d49df1e15", + "Drop Zone.tiff": "a7f8e3a2a431dbb3ce9e777040bb55e701cfe9ab9632e8cbb10359732bc8692e", + "large.png": "a02ddb255b1ae255c749bb837a759e2dee1c3b8ffb9bdc1167957761bd5a1b97", + "small.png": "1552b838dd88f60dee0795e20aaca652f9c558ecefc93500e895fbff77c2a4d5" + }, + "locked_relative_paths": [ + "大鱼人像美化/large.png", + "大鱼人像美化/small.png" + ], + "script_sha256": { + "preinstall": "b0562f7705c84a2f34e31df67a4f0d429e60083bab122dad99f56a4b266c0174", + "postinstall": "d147ccab1c428659fd2b0ed60e77399d1eb5910d1049dcdcbdbfc88fd3824300" + }, + "isolated_script_tests": [ + "fresh install", + "replace old locked thumbnail", + "partial copy failure restores old bytes and locks" + ], + "isolation_changes": [ + "Console identity/home redirected to task fixture", + "chown replaced with true", + "FxPlug verification skipped; formal app signature and discovery verified separately" + ], + "real_installer_execution": false, + "app_and_template_installed": "APP installed through signed delivery workflow; formal template files matched the specified installed category, not changed by running this PKG", + "fonts": "No editable third-party font dependency; promotional lettering baked into PNG. No optional font ZIP needed." +} diff --git a/docs/verification/v1.3.2/pkgforge-inspect.txt b/docs/verification/v1.3.2/pkgforge-inspect.txt new file mode 100644 index 0000000..fb1af89 --- /dev/null +++ b/docs/verification/v1.3.2/pkgforge-inspect.txt @@ -0,0 +1,11 @@ +识别到 2 个安装内容: +- FCP-BeautyGirl.app + 类型:FxPlug 插件 + 目录:/Library/Plug-Ins/FxPlug + 状态:已签名并公证,保留隔离属性,自动验证并修复注册;失败时打开 APP 所在目录 +- 大鱼磨皮瘦脸 + 类型:FCP 效果 + 目录:~/Movies/Motion Templates.localized/Effects.localized + 状态:非 APP,保留隔离属性 + +识别完成,未生成安装包。 diff --git a/docs/verification/v1.3.2/pkgforge.txt b/docs/verification/v1.3.2/pkgforge.txt new file mode 100644 index 0000000..b77eb93 --- /dev/null +++ b/docs/verification/v1.3.2/pkgforge.txt @@ -0,0 +1,27 @@ +识别到 2 个安装内容: +- FCP-BeautyGirl.app + 类型:FxPlug 插件 + 目录:/Library/Plug-Ins/FxPlug + 状态:已签名并公证,保留隔离属性,自动验证并修复注册;失败时打开 APP 所在目录 +- 大鱼磨皮瘦脸 + 类型:FCP 效果 + 目录:~/Movies/Motion Templates.localized/Effects.localized + 状态:非 APP,保留隔离属性 + +完成:/Users/dayu/Documents/PaintFCP/build/package-1.3.2/base/FCP-BeautyGirl-1.3.2.pkg + +本次唯一标识:com.dayu.fcppackage.b20261022104001942.tt2yn7vc +安装包版本:1.3.2(继承 FCP-BeautyGirl.app) +[正在准备内容] + 正在整理 2 个内容项 +[正在生成安装包] + 正在构建组件包 +[正在签名] + 正在使用 Developer ID Installer 签名 +[正在等待 Apple 公证] + 已提交 Apple,公证通常需要几分钟 +[正在粘附公证票据] + 公证已通过,正在粘附票据 +[正在验证结果] + 正在检查签名和安装包结构 + 签名、包结构与公证票据验证通过 diff --git a/docs/verification/v1.3.2/source-equivalence.json b/docs/verification/v1.3.2/source-equivalence.json new file mode 100644 index 0000000..c9e3267 --- /dev/null +++ b/docs/verification/v1.3.2/source-equivalence.json @@ -0,0 +1,24 @@ +{ + "baseline": "d1137edd836cc360c53d21c104a06f2435382441", + "signedSource": "767bba8ac11e9bf9268a1e02d1a06d3c120c46aa", + "renderAndStateFilesCompared": [ + "PGFaceSelectOSC.m", + "PGParameterIDs.h", + "PGInspectorState.m", + "PGBeautyTypes.h", + "PGBeautyRenderer.m", + "PGFaceProcessor.h", + "FCPBeautyGirlPlugIn.h", + "PGInspectorView.m", + "main.m", + "PGFaceProcessor.m", + "PGBeautyRenderer.h", + "PGInspectorState.h", + "FCPBeautyGirlPlugIn.m", + "PGInspectorView.h" + ], + "normalization": "Only entry class/header names, log subsystem and NSError domain differ; pixel and archived-state logic bytes unchanged.", + "regressionChecks": 1901, + "migrationUnitScenarios": 8, + "installerMigrationScenarios": "7 runtime scenarios passed; final package-block check runs after build." +} diff --git a/docs/项目交接.md b/docs/项目交接.md index 05e544d..75bdb72 100644 --- a/docs/项目交接.md +++ b/docs/项目交接.md @@ -2,7 +2,7 @@ 更新时间:2026-10-22,Asia/Shanghai。项目根目录:`/Users/dayu/Documents/PaintFCP`。 -当前正式 APP 为 **1.3.1(16)**,通用 Release、正式签名、Apple公证、票据、Gatekeeper和正式路径注册均已核验,临时APP副本已清理。1901项自动回归及1561项重叠真实帧几何ASan通过,V脸和新磨皮参数的最终宿主视觉仍由用户实测。签名源码 `7fa3298e022946e77dfd8cfba1bb5ff2c2a6361f`;本次PKG由本机PKG工坊CLI打包,包含用户指定已安装的「大鱼磨皮瘦脸」分类及4项模板资源。文章15593、GitHub和大鱼Release均已更新1.3.1,main与标签已同步;线上下载和恢复的具体核验边界见本版本回执。 +当前正式 APP 为 **1.3.2(17)**,工程、APP、插件服务及应用标识统一为 FCP-BeautyGirl。通用 Release、正式签名、Apple 公证、票据、Gatekeeper 和正式路径注册均已核验,旧 APP 与临时副本已清理。签名源码 `767bba8ac11e9bf9268a1e02d1a06d3c120c46aa`,发行标签 `v1.3.2`。1901 项自动回归、8 项 APP 身份迁移、8 项最终安装脚本身份迁移及模板隔离测试通过。滤镜 UUID、参数编号和归档保持原格式;新身份下的旧工程重开与最终原生视觉仍由用户实测。安装包基础由本机 PKG 工坊 CLI 生成,加入身份迁移后重新签名公证,包含指定已安装的「大鱼磨皮瘦脸」分类四项资源。文章 15593、GitHub 与大鱼 Release 沿用原入口,发布同步结果见本版回执。 代码由 **OpenAI + Codex 全程编写,站长仅参与调试和测试**。文章和仓库须保留这个表述,不使用“站长原创代码”等说法。免费版本无需账号、激活码或付费授权;“自动注册”指系统插件注册。 @@ -16,7 +16,7 @@ | 入口 | 用途 | | --- | --- | -| [1.3.1 交付回执](verification/v1.3.1/delivery-receipt.json) | 当前安装版、签名源码、公证、清理及原生视觉待测范围 | +| [1.3.2 交付回执](verification/v1.3.2/delivery-receipt.json) | 当前安装版、签名源码、公证、清理及原生视觉待测范围 | | [README](../README.md) | 产品介绍、使用与版本变化 | | [恢复说明](../RECOVERY.md) | 精确源码节点、依赖、远端恢复记录 | | [GitHub 同步说明](GitHub同步说明.md) | 两个远端、公开克隆及 Release 验证 | @@ -30,12 +30,12 @@ | 项目 | 当前值 | | --- | --- | | 显示名称 | 大鱼人像美化 | -| Xcode 产品名 | `beatygril`,保留现有拼写 | -| APP 版本 | `1.3.1` / `16`,arm64 与 x86_64 通用版本 | -| 唯一正式 APP | `/Library/Plug-Ins/FxPlug/beatygril.app` | -| 主 Bundle ID | `com.dayu.beatygril` | -| 嵌套插件 | `Contents/PlugIns/beatygril XPC Service.pluginkit` | -| 插件 Bundle ID | `com.dayu.beatygril.fxplug` | +| Xcode 产品名 | `FCP-BeautyGirl` | +| APP 版本 | `1.3.2` / `17`,arm64 与 x86_64 通用版本 | +| 唯一正式 APP | `/Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app` | +| 主 Bundle ID | `com.dayu.fcpbeautygirl` | +| 嵌套插件 | `Contents/PlugIns/FCP-BeautyGirl XPC Service.pluginkit` | +| 插件 Bundle ID | `com.dayu.fcpbeautygirl.fxplug` | | 滤镜 UUID | `FB14B0E6-59B2-4836-916F-CD0C559E9241` | | 自定义面板参数 ID | `210` | | FCP 效果分类 | 大鱼磨皮瘦脸 → 大鱼人像美化 | @@ -54,13 +54,13 @@ ## 源码结构 -Xcode 工程:`beatygril/beatygril.xcodeproj`。外层 scheme 为 `Wrapper Application`,插件 target 为 `XPC Service`。 +Xcode 工程:`FCP-BeautyGirl/FCP-BeautyGirl.xcodeproj`。外层 scheme 为 `FCP-BeautyGirl`,插件 target 为 `FCP-BeautyGirl XPC Service`。 -下表插件文件均位于 `beatygril/beatygril/Plugin/`。 +下表插件文件均位于 `FCP-BeautyGirl/FCP-BeautyGirl/Plugin/`。 | 文件或目录 | 职责与修改入口 | | --- | --- | -| `beatygrilPlugIn.m` | FxPlug 参数、宿主 action、图像适配、渲染状态与整体混合 | +| `FCPBeautyGirlPlugIn.m` | FxPlug 参数、宿主 action、图像适配、渲染状态与整体混合 | | `PGFaceProcessor.m` | Vision 检测、76 点、自动跟随、几何稳定、局部变形和几何蒙版 | | `PGBeautyRenderer.m` | 磨皮、肤色、细节、牙齿和眼睛等外观处理 | | `PGInspectorView.m` | AppKit 卡片、滑杆、数值框、开关、重置和连续提交 | @@ -120,9 +120,9 @@ Xcode 工程:`beatygril/beatygril.xcodeproj`。外层 scheme 为 `Wrapper Appl ```sh ./scripts/test.sh -PAINTFCP_HOST_ASAN=1 ./scripts/test-host.sh -PAINTFCP_OSC_ASAN=1 ./scripts/test-osc.sh -PAINTFCP_INSPECTOR_ASAN=1 ./scripts/test-inspector.sh +FCP_BEAUTYGIRL_HOST_ASAN=1 ./scripts/test-host.sh +FCP_BEAUTYGIRL_OSC_ASAN=1 ./scripts/test-osc.sh +FCP_BEAUTYGIRL_INSPECTOR_ASAN=1 ./scripts/test-inspector.sh python3 scripts/inspector-template.py audit 'Templates/大鱼人像美化/大鱼人像美化.moef' ``` @@ -157,7 +157,7 @@ python3 scripts/inspector-template.py audit 'Templates/大鱼人像美化/大鱼 | 资源 | 文件 | | --- | --- | | 项目封面 | `Assets/Artwork/portrait-cover.png` | -| APP 说明图 | `beatygril/beatygril/Wrapper Application/Resources/LauncherInfo.png` | +| APP 说明图 | `FCP-BeautyGirl/FCP-BeautyGirl/Wrapper Application/Resources/LauncherInfo.png` | | 大缩略图 | `Templates/大鱼人像美化/large.png`,640×360 | | 小缩略图 | `Templates/大鱼人像美化/small.png`,192×108 | | 生成记录 | `docs/design/artwork-manifest.json`、`artwork-prompt.json` | @@ -183,7 +183,7 @@ ls -lO "$HOME/Movies/Motion Templates.localized/Effects.localized/大鱼磨皮 python3 scripts/deliver.py ``` -脚本获取 `/Users/dayu/.codex/app-delivery-locks/com.dayu.beatygril.lock`,使用固定暂存 `/tmp/paintfcp-build-20261021`,记录到 `build/delivery/`。它构建通用 Release,由内到外签名 Framework、嵌套插件、外层 APP,验证严格签名,提交公证并要求 `Accepted`,粘附并验证票据,等待宿主退出后替换唯一正式路径,检查 Gatekeeper 与 PluginKit。默认停在 `HOST_VALIDATION_PENDING`,完成实际宿主验收及副本盘点后才回车进入清理。 +脚本同时获取新ID与product.json所列旧ID的共享交付锁,主锁为 `/Users/dayu/.codex/app-delivery-locks/com.dayu.fcpbeautygirl.lock`,使用固定暂存 `/tmp/fcp-beautygirl-release`,记录到 `build/delivery/`。它构建通用 Release,由内到外签名 Framework、嵌套插件、外层 APP,验证严格签名,提交公证并要求 `Accepted`,粘附并验证票据,等待宿主退出后替换唯一正式路径,检查 Gatekeeper 与 PluginKit。默认停在 `HOST_VALIDATION_PENDING`,完成实际宿主验收及副本盘点后才回车进入清理。 | 选项 | 用途 | | --- | --- | diff --git a/releases/1.3.2/FCP-BeautyGirl-1.3.2.pkg b/releases/1.3.2/FCP-BeautyGirl-1.3.2.pkg new file mode 100644 index 0000000..1f6e3f2 Binary files /dev/null and b/releases/1.3.2/FCP-BeautyGirl-1.3.2.pkg differ diff --git a/releases/1.3.2/RELEASE.md b/releases/1.3.2/RELEASE.md new file mode 100644 index 0000000..81e76bc --- /dev/null +++ b/releases/1.3.2/RELEASE.md @@ -0,0 +1,19 @@ +# FCP-BeautyGirl · 大鱼人像美化 1.3.2(17) + +统一工程、APP、插件服务、源码类名和应用标识的拼写。主 Bundle ID 为 `com.dayu.fcpbeautygirl`,插件 ID 为 `com.dayu.fcpbeautygirl.fxplug`,唯一正式安装路径为 `/Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app`。 + +安装器核对旧版的 Bundle ID、签名团队和滤镜 UUID 后迁移旧 APP;失败时恢复旧版,避免新旧服务并存。滤镜 UUID、参数编号和面板归档保留,渲染逻辑沿用 1.3.1。1901 项自动回归、8 项 APP 迁移测试、8 项安装器身份迁移测试,以及模板初装、更新与失败回滚隔离检查通过。更名后的旧工程重开与原生画面仍需用户实测。 + +- 文章:https://dayu.media/15593/ +- GitHub:https://github.com/dayumeida/FCP-BeautyGirl +- 大鱼备份:https://dayu.media/git/dayu/paintfcp-fxplug +- 发布标签:v1.3.2 +- 正式 APP 签名源码:767bba8ac11e9bf9268a1e02d1a06d3c120c46aa +- APP 公证:Accepted 559eedf0-048f-4d0e-b013-6a187695f2da +- PKG 公证:Accepted 4a831775-2317-4372-a4cc-102415657ea3 +- PKG:FCP-BeautyGirl-1.3.2.pkg,4,889,706 bytes +- SHA-256:20a88dacf68cc8812f5af86cfe94c1c0614414a68a019eaeac7ca9d078165ae1 + +本机 PKG 工坊 CLI 生成基础包,加入本产品的旧身份迁移与回滚段后,对最终包重新签名、公证和粘附票据。包含用户指定的「大鱼磨皮瘦脸」分类:大鱼人像美化.moef、Drop Zone.tiff、large.png 和 small.png。有效载荷与正式 APP、四个模板文件一致;恢复两张缩略图的锁定。签名、公证票据、Gatekeeper 和隔离安装脚本核验通过,未实际运行本 PKG 安装器;本机 APP 已通过正式交付流程安装。 + +安装前保存并退出 Final Cut Pro 和 Motion。Final Cut Pro 在「效果 → 大鱼磨皮瘦脸 → 大鱼人像美化」中使用。免费开源,无需激活码。代码由 OpenAI + Codex 全程编写,站长仅参与调试和测试。Apple SDK 及相关资源遵循各自许可。 diff --git a/releases/1.3.2/article-payload.json b/releases/1.3.2/article-payload.json new file mode 100644 index 0000000..de57772 --- /dev/null +++ b/releases/1.3.2/article-payload.json @@ -0,0 +1,44 @@ +{ + "title": "大鱼人像美化 1.3.2:FCP / Motion 免费开源自动美颜插件", + "status": "publish", + "content": "\n

大鱼人像美化是一款适用于 Final Cut Pro 与 Motion 的免费开源人像插件,把磨皮、美白和脸型调整放进同一块原生卡片面板。插件会识别当前画面中的人脸并自动跟随,无需先点击分析;你可以按素材需要调整强度,拖动滑杆时持续查看效果。

\n\n\n

代码由 OpenAI + Codex 全程编写,站长仅参与调试和测试。

\n\n\n\n
\"大鱼人像美化古风宣传封面,展示自动瘦脸、自动跟随与自动美颜\"
古风宣传插画;面部线条用于示意瘦脸与跟随功能。
\n\n\n\n

可以做什么

\n\n\n\n\n\n\n\n

自动识别负责定位处理区域,效果强度由你控制。建议先从较小的数值开始;多人交叉、严重遮挡和极端侧脸的结果需要结合具体素材检查。

\n\n\n\n

版本与下载

\n\n\n\n

当前版本 1.3.2(17),支持 macOS 13 或更高版本,包含 Apple Silicon 与 Intel 两种架构。下载包包含 FxPlug4 程序和一个配套 FCP 效果模板。APP 与 PKG 均已完成 Developer ID 正式签名、Apple 公证和票据粘附;安装包为所有用户免费资源。

\n\n\n\n

1.3.2 将工程、APP 与应用标识统一为 FCP-BeautyGirl;安装器会核对身份并迁移旧版程序,保留原滤镜 UUID 和参数存储格式。此版本沿用 1.3.1 的 V 脸下颌修复、微细锐化、连续五官保护和非肤色区保护。1901 项自动回归及安装迁移检查通过;本次更名后的旧工程重开与原生画面仍需结合实际素材检查。

\n\n\n\n

安装

\n\n\n\n

安装前先保存工作并退出 Final Cut Pro 和 Motion,安装期间保持关闭。运行本页提供的 PKG,按安装器提示完成安装;它会把程序安装到 /Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app,把配套效果安装到当前登录用户的 Motion Templates 目录,并自动验证和修复系统插件注册。

\n\n\n\n

本插件免费开源,无需激活码、付费注册或账号授权。主 APP 打开时显示说明图,窗口显示三秒后自动退出;宿主中的滤镜由独立服务运行。安装器还会重新锁定两张缩略图,避免被宿主自动替换。

\n\n\n\n
\"主程序说明页:免费开源,自动注册,无需激活码,启动三秒后自动退出\"
\n\n\n\n

使用

\n\n\n\n

Final Cut Pro:打开效果浏览器,找到大鱼磨皮瘦脸分类,将大鱼人像美化添加到人像片段。Motion:在原生滤镜资源库的大鱼分类中选择大鱼人像美化。

\n\n\n\n

在检查器中按需调节磨皮、美白和脸型。每项左侧的眼睛可暂时停用该项,数值会保留;右侧回转箭头用于重置。人脸会随画面自动更新,开启检测框预览可以检查跟随位置。多人模式建议先用短片段观察稳定性。

\n\n\n\n

常见问题

\n\n\n\n

如果效果没有出现或显示红色警告,先退出 Final Cut Pro 和 Motion,再打开一次主 APP,等待其自动退出,然后重新启动宿主。可在“终端”执行下面这条命令:

\n\n\n\n
open '/Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app'
\n\n\n\n

该流程可重新触发系统注册,并不能解决所有报错。仍异常时请核对 macOS、宿主版本及安装包完整性;不要只复制单个 .moef 文件,配套程序与 Drop Zone.tiff 也需要保留。当前仅提供上述一个效果模板。

\n\n\n\n

源码与开源许可

\n\n\n\n

公开源码仓库:GitHub · FCP-BeautyGirl,可直接浏览源码、完整提交历史和版本标签。1.3.2 Release 提供正式签名公证安装包。大鱼站仓库作为备份保留:paintfcp-fxplug(该入口仍受站点登录与会员网关限制)。也可下载 1.3.2 源码快照 ZIP,源码与安装包均免费提供。

\n\n\n\n

项目代码按仓库中的 MIT 许可文件提供,源码包含 Xcode 工程、Objective-C / Metal 实现、模板及构建与验证说明。Apple SDK、运行框架及样本按各自许可使用,详见源码中的 LICENSE 和 THIRD_PARTY_NOTICES.md。自行编译需要 Xcode 与 FxPlug SDK;证书和签名私钥不包含在源码中。

\n\n\n\n
\"\"
\n", + "excerpt": "1.3.2 将工程、APP 与应用 ID 统一为 FCP-BeautyGirl,安装自动迁移旧版。保留 V 脸下颌修复和磨皮参数,提供免费开源源码及签名公证安装包。", + "seo": { + "title": { + "enabled": true, + "value": "大鱼人像美化 1.3.2|FCP / Motion 免费开源自动美颜插件" + }, + "description": { + "enabled": true, + "value": "大鱼人像美化是适用于 Final Cut Pro 和 Motion 的免费开源 FxPlug4 插件。1.3.2 将工程、APP 和应用 ID 统一为 FCP-BeautyGirl,自动迁移旧版;保留 V 脸下颌修复及磨皮保护参数。提供 Apple 签名公证安装包和源码,无需激活码,附安装使用说明。" + }, + "keywords": { + "enabled": true, + "value": "大鱼人像美化,FCP美颜插件,Motion磨皮,自动瘦脸,自动跟随,FxPlug4,免费开源" + } + }, + "resource": { + "cao_info": [ + { + "title": "版本", + "desc": "1.3.2(17)" + }, + { + "title": "宿主", + "desc": "Final Cut Pro / Motion" + }, + { + "title": "系统", + "desc": "macOS 13 或更高版本" + }, + { + "title": "架构", + "desc": "Apple Silicon / Intel" + }, + { + "title": "授权", + "desc": "免费开源,无需激活码" + } + ] + } +} diff --git a/releases/1.3.2/article.html b/releases/1.3.2/article.html new file mode 100644 index 0000000..0e58040 --- /dev/null +++ b/releases/1.3.2/article.html @@ -0,0 +1,112 @@ + +

大鱼人像美化是一款适用于 Final Cut Pro 与 Motion 的免费开源人像插件,把磨皮、美白和脸型调整放进同一块原生卡片面板。插件会识别当前画面中的人脸并自动跟随,无需先点击分析;你可以按素材需要调整强度,拖动滑杆时持续查看效果。

+ + +

代码由 OpenAI + Codex 全程编写,站长仅参与调试和测试。

+ + + +
大鱼人像美化古风宣传封面,展示自动瘦脸、自动跟随与自动美颜
古风宣传插画;面部线条用于示意瘦脸与跟随功能。
+ + + +

可以做什么

+ + + + + + + +

自动识别负责定位处理区域,效果强度由你控制。建议先从较小的数值开始;多人交叉、严重遮挡和极端侧脸的结果需要结合具体素材检查。

+ + + +

版本与下载

+ + + +

当前版本 1.3.2(17),支持 macOS 13 或更高版本,包含 Apple Silicon 与 Intel 两种架构。下载包包含 FxPlug4 程序和一个配套 FCP 效果模板。APP 与 PKG 均已完成 Developer ID 正式签名、Apple 公证和票据粘附;安装包为所有用户免费资源。

+ + + +

1.3.2 将工程、APP 与应用标识统一为 FCP-BeautyGirl;安装器会核对身份并迁移旧版程序,保留原滤镜 UUID 和参数存储格式。此版本沿用 1.3.1 的 V 脸下颌修复、微细锐化、连续五官保护和非肤色区保护。1901 项自动回归及安装迁移检查通过;本次更名后的旧工程重开与原生画面仍需结合实际素材检查。

+ + + +

安装

+ + + +

安装前先保存工作并退出 Final Cut Pro 和 Motion,安装期间保持关闭。运行本页提供的 PKG,按安装器提示完成安装;它会把程序安装到 /Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app,把配套效果安装到当前登录用户的 Motion Templates 目录,并自动验证和修复系统插件注册。

+ + + +

本插件免费开源,无需激活码、付费注册或账号授权。主 APP 打开时显示说明图,窗口显示三秒后自动退出;宿主中的滤镜由独立服务运行。安装器还会重新锁定两张缩略图,避免被宿主自动替换。

+ + + +
主程序说明页:免费开源,自动注册,无需激活码,启动三秒后自动退出
+ + + +

使用

+ + + +

Final Cut Pro:打开效果浏览器,找到大鱼磨皮瘦脸分类,将大鱼人像美化添加到人像片段。Motion:在原生滤镜资源库的大鱼分类中选择大鱼人像美化。

+ + + +

在检查器中按需调节磨皮、美白和脸型。每项左侧的眼睛可暂时停用该项,数值会保留;右侧回转箭头用于重置。人脸会随画面自动更新,开启检测框预览可以检查跟随位置。多人模式建议先用短片段观察稳定性。

+ + + +

常见问题

+ + + +

如果效果没有出现或显示红色警告,先退出 Final Cut Pro 和 Motion,再打开一次主 APP,等待其自动退出,然后重新启动宿主。可在“终端”执行下面这条命令:

+ + + +
open '/Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app'
+ + + +

该流程可重新触发系统注册,并不能解决所有报错。仍异常时请核对 macOS、宿主版本及安装包完整性;不要只复制单个 .moef 文件,配套程序与 Drop Zone.tiff 也需要保留。当前仅提供上述一个效果模板。

+ + + +

源码与开源许可

+ + + +

公开源码仓库:GitHub · FCP-BeautyGirl,可直接浏览源码、完整提交历史和版本标签。1.3.2 Release 提供正式签名公证安装包。大鱼站仓库作为备份保留:paintfcp-fxplug(该入口仍受站点登录与会员网关限制)。也可下载 1.3.2 源码快照 ZIP,源码与安装包均免费提供。

+ + + +

项目代码按仓库中的 MIT 许可文件提供,源码包含 Xcode 工程、Objective-C / Metal 实现、模板及构建与验证说明。Apple SDK、运行框架及样本按各自许可使用,详见源码中的 LICENSE 和 THIRD_PARTY_NOTICES.md。自行编译需要 Xcode 与 FxPlug SDK;证书和签名私钥不包含在源码中。

+ + + +
+ diff --git a/scripts/package-release.py b/scripts/package-release.py new file mode 100644 index 0000000..e764599 --- /dev/null +++ b/scripts/package-release.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +"""Package the installed product with local PKG Forge, then sign/notarize its migration update.""" +import argparse, datetime, hashlib, json, os, pathlib, plistlib, shutil, subprocess, sys +from installer_identity import inject_migration +from product_identity import PRODUCT, ROOT, delivery_locks, validate_app + +def run(args, log=None, env=None): + result = subprocess.run(args, cwd=ROOT, text=True, capture_output=True, env=env) + if log: + pathlib.Path(log).write_text(result.stdout + result.stderr) + result.check_returncode() + return result.stdout + +version = PRODUCT['version'] +work = ROOT / 'build' / ('package-' + version) +release = ROOT / 'releases' / version +proof = ROOT / 'docs/verification' / ('v' + version) +package_name = PRODUCT['name'] + '-' + version + '.pkg' +final = release / package_name +keychain = '/Users/dayu/Library/Keychains/login.keychain-db' +installer_identity = 'Developer ID Installer: zhong luo (B3W25FX5H5)' +cli = '/Applications/PKG工坊.app/Contents/MacOS/PKG工坊' +source_template = pathlib.Path('/Users/dayu/Movies/Motion Templates.localized/Effects.localized/大鱼磨皮瘦脸') +parser = argparse.ArgumentParser(description=__doc__) +parser.add_argument('--verify-existing', action='store_true', help='Resume verification of the already Accepted, stapled final package without rebuilding or resubmitting it.') +args = parser.parse_args() + +with delivery_locks(): + if not args.verify_existing: + if work.exists() or final.exists(): + raise SystemExit('Packaging work or final package already exists; inspect before retrying') + work.mkdir(parents=True) + release.mkdir(parents=True, exist_ok=True) + proof.mkdir(parents=True, exist_ok=True) + formal = pathlib.Path(PRODUCT['installationPath']) + info = validate_app(formal, PRODUCT['bundleIdentifier']) + assert info['CFBundleShortVersionString'] == version and info['CFBundleVersion'] == PRODUCT['build'] + assert installer_identity in run(['security', 'find-identity', '-v', '-p', 'basic', keychain]) + template = work / 'input/大鱼磨皮瘦脸' + manifest = [] + for source in sorted(source_template.rglob('*')): + if source.is_file() and source.name != '.DS_Store': + target = template / source.relative_to(source_template) + target.parent.mkdir(parents=True, exist_ok=True) + shutil.copyfile(source, target) + if source.stat().st_flags & 2: + run(['chflags', 'uchg', str(target)]) + manifest.append({'file': str(source.relative_to(source_template)), 'sha256': hashlib.sha256(source.read_bytes()).hexdigest(), 'locked': bool(source.stat().st_flags & 2)}) + assert len(manifest) == 4 and len(list(template.rglob('*.moef'))) == 1 + base = work / 'base' / package_name + base.parent.mkdir() + print('PKG Forge: generating signed/notarized base installer from the formal APP and installed Motion category.', flush=True) + run([cli, 'inspect', str(formal), str(template)], proof / 'pkgforge-inspect.txt') + run([cli, 'package', '--name', PRODUCT['name'], '--version', version, '--identifier-prefix', 'com.dayu.fcppackage', '--signing-identity', installer_identity, '--notary-profile', 'codex-dayu-notary-B3W25FX5H5', '--keychain', keychain, '--output', str(base), str(formal), str(template)], proof / 'pkgforge.txt') + run(['pkgutil', '--check-signature', str(base)]) + run(['xcrun', 'stapler', 'validate', str(base)]) + expanded = work / 'expanded' + run(['pkgutil', '--expand', str(base), str(expanded)]) + script = expanded / 'Payload.pkg/Scripts/postinstall' + script.write_text(inject_migration(script.read_text())) + run(['/bin/zsh', '-n', str(script)]) + # PKG Forge owns the payload/template/update scripts. Only the product's + # identity migration is added, then the complete result is signed afresh. + unsigned = work / 'migration-unsigned.pkg' + run(['pkgutil', '--flatten', str(expanded), str(unsigned)]) + run(['productsign', '--sign', installer_identity, '--keychain', keychain, '--timestamp', str(unsigned), str(final)], proof / 'pkg-signing.txt') + print('Submitting the final migration installer to Apple notarization.', flush=True) + result = json.loads(run(['xcrun', 'notarytool', 'submit', str(final), '--keychain-profile', 'codex-dayu-notary-B3W25FX5H5', '--keychain', keychain, '--wait', '--output-format', 'json'])) + (proof / 'pkg-notarization.json').write_text(json.dumps(result, indent=2) + '\n') + if result.get('status') != 'Accepted': + raise RuntimeError('Final migration package notarization was not Accepted') + run(['xcrun', 'stapler', 'staple', str(final)], proof / 'pkg-staple.txt') + run(['xcrun', 'stapler', 'validate', str(final)]) + run(['pkgutil', '--check-signature', str(final)], proof / 'pkg-signature.txt') + run(['spctl', '-a', '-vv', '-t', 'install', str(final)], proof / 'pkg-gatekeeper.txt') + else: + if not work.exists() or not final.exists(): + raise RuntimeError('No prepared package to verify') + formal = pathlib.Path(PRODUCT['installationPath']) + info = validate_app(formal, PRODUCT['bundleIdentifier']) + assert info['CFBundleShortVersionString'] == version and info['CFBundleVersion'] == PRODUCT['build'] + result = json.loads((proof / 'pkg-notarization.json').read_text()) + assert result['status'] == 'Accepted' + run(['xcrun', 'stapler', 'validate', str(final)]) + run(['pkgutil', '--check-signature', str(final)]) + run(['spctl', '-a', '-vv', '-t', 'install', str(final)]) + template = work / 'input/大鱼磨皮瘦脸' + manifest = [{'file': str(p.relative_to(template)), 'sha256': hashlib.sha256(p.read_bytes()).hexdigest(), 'locked': bool(p.stat().st_flags & 2)} for p in sorted(template.rglob('*')) if p.is_file()] + assert len(manifest) == 4 + full = work / 'full' + try: + run(['pkgutil', '--expand-full', str(final), str(full)]) + run([sys.executable, 'scripts/verify-release-package.py', '--version', version, '--build', PRODUCT['build'], '--expanded', str(full), '--fixture', 'build/pkg-isolation-' + version], proof / 'pkg-verification-output.txt') + env = dict(os.environ, FCP_BEAUTYGIRL_POSTINSTALL=str(full / 'Payload.pkg/Scripts/postinstall')) + run([sys.executable, 'tests/installer_identity_tests.py'], proof / 'installer-tests.txt', env=env) + finally: + if full.exists(): + run(['chflags', '-R', 'nouchg', str(full)]) + shutil.rmtree(full) + receipt = {'status': 'verified', 'version': version, 'build': PRODUCT['build'], 'package': str(final.relative_to(ROOT)), 'bytes': final.stat().st_size, 'sha256': hashlib.sha256(final.read_bytes()).hexdigest(), 'sourceApp': str(formal), 'sourceCommit': json.loads((ROOT / 'build/delivery/delivery-receipt.json').read_text())['source']['commit'], 'motionSource': str(source_template), 'motionFiles': manifest, 'packager': cli, 'productMigration': 'PKG Forge base plus identity-checked backup/rollback block; final package freshly signed, notarized and stapled', 'notarization': result, 'realInstallerExecuted': False} + (proof / 'pkg-delivery.json').write_text(json.dumps(receipt, ensure_ascii=False, indent=2) + '\n') + run(['chflags', '-R', 'nouchg', str(work)]) + shutil.rmtree(work) + print(json.dumps(receipt, ensure_ascii=False), flush=True) diff --git a/scripts/update-release-article.py b/scripts/update-release-article.py index 8513aca..e89de11 100644 --- a/scripts/update-release-article.py +++ b/scripts/update-release-article.py @@ -3,6 +3,7 @@ Only --publish performs writes. Credentials stay in memory and go only to Dayu. """ import argparse, base64, hashlib, html, json, pathlib, ssl, subprocess, urllib.parse, urllib.request, uuid, zipfile +from product_identity import PRODUCT parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('--version', required=True) @@ -49,8 +50,8 @@ if not args.publish: raise SystemExit(0) baseline = json.loads((work / 'article-before.json').read_text())['article'] payload = json.loads((release / 'article-payload.json').read_text()) -package = release / ('大鱼人像美化-' + args.version + '.pkg') -source = root / 'build' / ('Dayu-Portrait-' + args.version + '-source.zip') +package = release / (PRODUCT['name'] + '-' + args.version + '.pkg') +source = root / 'build' / (PRODUCT['name'] + '-' + args.version + '-source.zip') with zipfile.ZipFile(source) as z: assert z.testzip() is None and any(n.endswith('/Wrapper Application/AppDelegate.m') for n in z.namelist()) assert not any(n.endswith(('.app', '.pkg', '.p12', '.key', '.env')) for n in z.namelist()) @@ -77,7 +78,7 @@ assert '{{' not in payload['content'] if not state.get('response'): boundary = 'DayuRelease' + state['idempotencyKey'].replace('-', '') parts = [] - for name, data, filename, mime in [('payload', json.dumps(payload, ensure_ascii=False).encode(), None, 'application/json'), ('package', package.read_bytes(), 'Dayu-Portrait-' + args.version + '.pkg', 'application/octet-stream')]: + for name, data, filename, mime in [('payload', json.dumps(payload, ensure_ascii=False).encode(), None, 'application/json'), ('package', package.read_bytes(), package.name, 'application/octet-stream')]: head = '--' + boundary + '\r\nContent-Disposition: form-data; name="' + name + '"' if filename: head += '; filename="' + filename + '"' diff --git a/scripts/verify-release-package.py b/scripts/verify-release-package.py index b181059..3980fbe 100644 --- a/scripts/verify-release-package.py +++ b/scripts/verify-release-package.py @@ -2,10 +2,11 @@ """Verify an expanded release payload and run template-only update/rollback in isolation.""" from pathlib import Path import argparse,hashlib,json,os,plistlib,shutil,subprocess,sys +from product_identity import PRODUCT root=Path(__file__).resolve().parent.parent parser=argparse.ArgumentParser(description=__doc__) -parser.add_argument('--version',default='1.2.0') -parser.add_argument('--build',default='14') +parser.add_argument('--version',default=PRODUCT['version']) +parser.add_argument('--build',default=PRODUCT['build']) parser.add_argument('--expanded',default='build/pkg-expanded') parser.add_argument('--fixture',default='build/pkg-isolation') args=parser.parse_args() @@ -33,7 +34,10 @@ base=root/args.fixture;base.mkdir(exist_ok=False) volume=base/'volume';home=base/'user';home.mkdir() start=post.index('CURRENT_ACTIVE=0') isolated='#!/bin/zsh\nset -eu\nTARGET_VOLUME="'+str(volume)+'"\nCONSOLE_USER="'+os.getlogin()+'"\nCONSOLE_UID="'+str(os.getuid())+'"\nCONSOLE_GROUP="staff"\nUSER_HOME="'+str(home)+'"\n'+post[start:] -isolated=isolated[:isolated.index('fx_verify_app "${TARGET_VOLUME%/}/Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app"')]+'exit 0\n' +stop = isolated.find('# FCP-BeautyGirl identity migration.') +if stop < 0: + stop = isolated.index('fx_verify_app "${TARGET_VOLUME%/}/Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app"') +isolated=isolated[:stop]+'exit 0\n' isolated=isolated.replace('/usr/sbin/chown -R','/usr/bin/true') new_copy=next(l for l in isolated.splitlines() if l.startswith('/usr/bin/ditto "${TARGET_VOLUME%/}')) def refill(): @@ -66,7 +70,7 @@ assert files(final)==original assert all((final/n).stat().st_flags & 2 for n in ['large.png','small.png']) backups=list((home/'Library/Application Support/PKGForge/Template Backups').rglob('.locked-paths')) assert backups and all('/Movies/' not in str(x) for x in backups) -package=root/('releases/'+args.version+'/大鱼人像美化-'+args.version+'.pkg') +package=root/('releases/'+args.version+'/'+PRODUCT['name']+'-'+args.version+'.pkg') info=plistlib.loads((packed/'Contents/Info.plist').read_bytes()) assert info['CFBundleShortVersionString']==args.version and info['CFBundleVersion']==args.build report={'package':str(package.relative_to(root)),'bytes':package.stat().st_size,'sha256':sha(package),'version':args.version,'build':args.build,'app_files_compared':len(files(formal)),'app_byte_and_symlink_equality':True,'template_files':{n:sha(template/n) for n in runtime},'locked_relative_paths':['大鱼人像美化/large.png','大鱼人像美化/small.png'],'script_sha256':{'preinstall':sha(expanded/'Scripts/preinstall'),'postinstall':sha(expanded/'Scripts/postinstall')},'isolated_script_tests':['fresh install','replace old locked thumbnail','partial copy failure restores old bytes and locks'],'isolation_changes':['Console identity/home redirected to task fixture','chown replaced with true','FxPlug verification skipped; formal app signature and discovery verified separately'],'real_installer_execution':False,'app_and_template_installed':'APP installed through signed delivery workflow; formal template files matched the specified installed category, not changed by running this PKG','fonts':'No editable third-party font dependency; promotional lettering baked into PNG. No optional font ZIP needed.'} diff --git a/tests/installer_identity_tests.py b/tests/installer_identity_tests.py new file mode 100644 index 0000000..9576ad4 --- /dev/null +++ b/tests/installer_identity_tests.py @@ -0,0 +1,104 @@ +"""Run the generated installer migration in isolated volumes, never live paths.""" +import os, pathlib, plistlib, shlex, subprocess, sys, tempfile, unittest +ROOT = pathlib.Path(__file__).resolve().parent.parent +sys.path.insert(0, str(ROOT / 'scripts')) +from installer_identity import inject_migration, PRODUCT + +class InstallerIdentityTests(unittest.TestCase): + def setUp(self): + self.tmp = tempfile.TemporaryDirectory(prefix='beautygirl-installer-test.') + self.root = pathlib.Path(self.tmp.name) + self.old = self.root / pathlib.Path(PRODUCT['legacyProducts'][0]['installationPath']).relative_to('/') + self.new = self.root / pathlib.Path(PRODUCT['installationPath']).relative_to('/') + for app, info in [(self.old, PRODUCT['legacyProducts'][0]), (self.new, PRODUCT)]: + (app / 'Contents/PlugIns' / info['pluginName'] / 'Contents').mkdir(parents=True) + (app / 'Contents/Info.plist').write_bytes(plistlib.dumps({'CFBundleIdentifier': info['bundleIdentifier']})) + (app / 'Contents/PlugIns' / info['pluginName'] / 'Contents/Info.plist').write_bytes(plistlib.dumps({'ProPlugPlugInList': [{'uuid': PRODUCT['filterUUID']}]})) + (app / 'payload-byte').write_bytes(b'original signed payload stand-in') + self.signer = self.root / 'codesign-fixture' + self.signer.write_text('#!/bin/zsh\napp="${argv[-1]}"\nif [[ -f "$app/.invalid" ]]; then exit 1; fi\n/bin/echo TeamIdentifier=B3W25FX5H5 >&2\n') + self.signer.chmod(0o755) + + def tearDown(self): + self.tmp.cleanup() + + def run_installer(self, fail_registration=False): + base = '''#!/bin/zsh +set -eu +TARGET_VOLUME=@@VOLUME@@ +CONSOLE_UID=501 +CONSOLE_USER=fixture +FX_FAILURE=0 +fx_verify_app() { + /bin/echo "$1" >> @@LOG@@ + if [[ "$1" == "$BG_NEW_APP" && @@FAIL@@ == 1 ]]; then return 1; fi + return 0 +} +trap - EXIT +fx_verify_app "${TARGET_VOLUME}/Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app" || FX_FAILURE=1 +if (( FX_FAILURE )); then exit 1; fi +exit 0 +''' + base = base.replace('@@VOLUME@@', shlex.quote(str(self.root))).replace('@@LOG@@', shlex.quote(str(self.root / 'registration-calls'))).replace('@@FAIL@@', '1' if fail_registration else '0') + code = inject_migration(base) + # Only signature and user-session registration are isolated. Identity + # parsing, file moves, deletes, traps and rollback run unchanged. + code = code.replace('/usr/bin/codesign', shlex.quote(str(self.signer))) + code = code.replace('/bin/launchctl asuser', '/usr/bin/true asuser') + script = self.root / 'postinstall' + script.write_text(code) + subprocess.run(['/bin/zsh', '-n', str(script)], check=True, capture_output=True) + return subprocess.run(['/bin/zsh', str(script)], text=True, capture_output=True) + + def test_upgrade_keeps_new_and_removes_old_and_recovery_copy(self): + self.assertEqual(self.run_installer().returncode, 0) + self.assertTrue(self.new.exists()) + self.assertFalse(self.old.exists()) + self.assertFalse(list((self.root / 'Library/Application Support').rglob('*.app'))) + + def test_registration_failure_restores_old_and_removes_new(self): + original = (self.old / 'payload-byte').read_bytes() + result = self.run_installer(True) + self.assertNotEqual(result.returncode, 0) + self.assertFalse(self.new.exists()) + self.assertEqual((self.old / 'payload-byte').read_bytes(), original) + self.assertIn(str(self.old), (self.root / 'registration-calls').read_text()) + + def test_invalid_new_signature_does_not_touch_old(self): + (self.new / '.invalid').touch() + self.assertNotEqual(self.run_installer().returncode, 0) + self.assertTrue(self.old.exists()) + + def test_foreign_old_bundle_id_is_preserved(self): + (self.old / 'Contents/Info.plist').write_bytes(plistlib.dumps({'CFBundleIdentifier': 'com.unrelated.product'})) + self.assertNotEqual(self.run_installer().returncode, 0) + self.assertTrue(self.old.exists()) + + def test_invalid_old_signature_is_preserved(self): + (self.old / '.invalid').touch() + self.assertNotEqual(self.run_installer().returncode, 0) + self.assertTrue(self.old.exists()) + + def test_fresh_install_and_repeat_are_idempotent(self): + import shutil + shutil.rmtree(self.old) + self.assertEqual(self.run_installer().returncode, 0) + self.assertEqual(self.run_installer().returncode, 0) + self.assertTrue(self.new.exists()) + + def test_unrelated_neighbor_is_preserved(self): + unrelated = self.old.parent / 'OtherProduct.app' + unrelated.mkdir() + self.assertEqual(self.run_installer().returncode, 0) + self.assertTrue(unrelated.exists()) + + @unittest.skipUnless(os.environ.get('FCP_BEAUTYGIRL_POSTINSTALL'), 'Final PKG is not built yet') + def test_final_package_contains_the_tested_migration_block(self): + actual = pathlib.Path(os.environ['FCP_BEAUTYGIRL_POSTINSTALL']).read_text() + generated = inject_migration('\ntrap - EXIT\nfx_verify_app candidate\nexit 0\n') + start = generated.index('# FCP-BeautyGirl identity migration.') + end = generated.rindex('\nfx_verify_app ') + self.assertIn(generated[start:end], actual) + +if __name__ == '__main__': + unittest.main()