Align 2.4.0 pin tracking and pupil-based eyes; add sequence and target-field regressions

这个提交包含在:
dayu 于 2026-10-23 00:09:32 +08:00 提交
1 父节点 524f536a53
当前提交 75e7f4a00d
26 个文件已更改
+16839 -87

没有与搜索条件相匹配的文件

+8
查看文件
@@ -9,7 +9,10 @@ parser=argparse.ArgumentParser(description=__doc__)
parser.add_argument('--wait-for-hosts',action='store_true',help='After notarization, retain the delivery lock and wait for Motion/FCP to exit naturally before installing; never control or quit either host.')
parser.add_argument('--user-tests',action='store_true',help='After non-UI installed-app verification, record native effect QA as pending user testing and clean known generated copies without the interactive validation prompt.')
parser.add_argument('--resume-prepared',action='store_true',help='Resume an already Accepted and stapled staged app after checking its recorded signature, version and unchanged product source.')
parser.add_argument('--prepare-only',action='store_true',help='Build, sign, notarize and staple a staged Release, then release the delivery lock without replacing the installed APP or touching running hosts.')
args=parser.parse_args()
class PreparedOnly(Exception):
pass
bundle_id=PRODUCT['bundleIdentifier']
locks=delivery_locks();locks.__enter__()
staging=pathlib.Path('/tmp/fcp-beautygirl-release')
@@ -133,6 +136,9 @@ try:
# old installed version open. This removes only this known staging path.
unregister_generated_plugin(plugin)
save_receipt('prepared',signingVerified=True,stapleValidated=True,stagingPluginUnregistered=True)
if args.prepare_only:
print(json.dumps({'status':'prepared','path':str(app),'notarization':result,'installedAppUnchanged':True}),flush=True)
raise PreparedOnly()
# A live host retains its old XPC/OSC connection. Replacing the bundle while
# that connection exists can deadlock host shutdown or select stale code.
await_host_exit()
@@ -184,6 +190,8 @@ try:
removed=migration.cleanup()+cleanup_known_products()
save_receipt('installed_user_qa_pending' if args.user_tests else 'complete',removedGeneratedPaths=removed)
print('Delivery staging and notarization archive cleaned.',flush=True)
except PreparedOnly:
pass
except BaseException as error:
save_receipt('failed',failure={'type':type(error).__name__,'message':str(error)})
raise
+4 -17
查看文件
@@ -17,23 +17,10 @@
- (NSArray<VNFaceObservation *> *)observationsInImage:(CIImage *)image settings:(PGBeautySettings)s error:(NSError **)error {return self.authoredDetections?:@[];}
@end
static NSDictionary *Geometry(CGRect box, double localJitter, BOOL has76) {
NSMutableDictionary *g=[@{@"box":@[@(box.origin.x),@(box.origin.y),@(box.size.width),@(box.size.height)]} mutableCopy];
for(NSString *name in @[@"leftEye",@"rightEye",@"leftBrow",@"rightBrow",@"nose",@"lips",@"mouth",@"contour"]){
double x=[name isEqual:@"rightEye"]?.7:.3;
g[name]=@[@[@(box.origin.x+(x+localJitter)*box.size.width),@(box.origin.y+.65*box.size.height)]];
}
if(has76){
NSMutableArray *p=[NSMutableArray new];
for(NSUInteger i=0;i<76;i++)[p addObject:@[@.5,@.5]];
p[6]=@[@.34,@.65];p[13]=@[@.66,@.65];
p[46]=@[@.5,@.68];p[47]=@[@.5,@.60];p[48]=@[@.5,@.52];p[49]=@[@.5,@.45];p[52]=@[@.5,@.42];
p[29]=@[@.5,@.39];p[36]=@[@.54,@.36];p[37]=@[@.5,@.35];p[38]=@[@.46,@.36];
p[26]=@[@.42,@.40];p[34]=@[@.58,@.40];p[35]=@[@.57,@.36];p[39]=@[@.43,@.36];
for(NSUInteger i=0;i<17;i++){double t=M_PI+M_PI*i/16;p[59+i]=@[@(.5+.29*cos(t)),@(.65+.43*sin(t))];}
for(NSUInteger i=0;i<76;i++)p[i]=@[@(box.origin.x+([p[i][0] doubleValue]+localJitter)*box.size.width),@(box.origin.y+[p[i][1] doubleValue]*box.size.height)];
g[@"points76"]=p;
}
#import "../tests/tracking_fixture.h"
static NSDictionary *Geometry(CGRect box,double jitter,BOOL has76) {
NSMutableDictionary *g=[PGTrackingTestGeometry(box,jitter) mutableCopy];
if(!has76)[g removeObjectForKey:@"points76"];
return g;
}
static NSDictionary *Live(PGRealtimeFaceTracker *t,NSArray *g,PGBeautySettings s,int64_t tick){return[t resultForGeometry:g settings:s time:CMTimeMake(tick,1000) extent:CGRectMake(0,0,256,256)];}
+32 -6
查看文件
@@ -23,19 +23,45 @@ 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.')
parser.add_argument('--prepared-app', action='store_true', help='Package the exact Accepted/stapled delivery staging APP before host exit, keeping the existing formal installation untouched.')
args = parser.parse_args()
def package_app():
formal = pathlib.Path(PRODUCT['installationPath'])
receipt = json.loads((ROOT / 'build/delivery/delivery-receipt.json').read_text())
hashes = lambda text: [line for line in text.splitlines() if line.startswith('CDHash=')]
source = receipt['source']['commit']
if run(['git', 'diff', source, '--', PRODUCT['sourceRoot'], 'product.json']).strip():
raise RuntimeError('Product source changed since the signed delivery')
if not args.prepared_app:
if receipt.get('status') not in ('installed', 'installed_user_qa_pending', 'complete'):
raise RuntimeError('The recorded delivery is not installed; use --prepared-app for a prepared Release')
signature = subprocess.run(['codesign', '-dvvv', str(formal)], check=True, text=True, capture_output=True).stderr
if not hashes(signature) or hashes(signature) != hashes(receipt.get('installedSignature', '')):
raise RuntimeError('Installed APP signature differs from the recorded delivery source')
return formal
app = pathlib.Path('/tmp/fcp-beautygirl-release/Build/Products/Release') / (PRODUCT['name'] + '.app')
if receipt.get('status') != 'prepared' or receipt.get('notarization', {}).get('status') != 'Accepted':
raise RuntimeError('Prepared delivery is not Accepted and ready')
if receipt.get('version') != version or receipt.get('build') != PRODUCT['build']:
raise RuntimeError('Prepared delivery version mismatch')
signature = subprocess.run(['codesign', '-dvvv', str(app)], check=True, text=True, capture_output=True).stderr
if not hashes(signature) or hashes(signature) != hashes(receipt['stagingSignature']):
raise RuntimeError('Prepared APP signature differs from the Accepted delivery')
run(['xcrun', 'stapler', 'validate', str(app)])
return app
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'])
formal = package_app()
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])
work.mkdir(parents=True)
release.mkdir(parents=True, exist_ok=True)
proof.mkdir(parents=True, exist_ok=True)
template = work / 'input/大鱼磨皮瘦脸'
manifest = []
for source in sorted(source_template.rglob('*')):
@@ -76,7 +102,7 @@ with delivery_locks():
else:
if not work.exists() or not final.exists():
raise RuntimeError('No prepared package to verify')
formal = pathlib.Path(PRODUCT['installationPath'])
formal = package_app()
info = validate_app(formal, PRODUCT['bundleIdentifier'])
assert info['CFBundleShortVersionString'] == version and info['CFBundleVersion'] == PRODUCT['build']
result = json.loads((proof / 'pkg-notarization.json').read_text())
@@ -90,7 +116,7 @@ with delivery_locks():
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')
run([sys.executable, 'scripts/verify-release-package.py', '--version', version, '--build', PRODUCT['build'], '--expanded', str(full), '--fixture', 'build/pkg-isolation-' + version, '--source-app', str(formal)], 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:
可执行文件
+15
查看文件
@@ -0,0 +1,15 @@
#!/bin/zsh
set -euo pipefail
cd "${0:A:h:h}"
eye_test_output=$(mktemp -d /tmp/fcp-beautygirl-eye-tests.XXXXXX)
cleanup_eye_tests() {
python3 -c 'import shutil,sys; shutil.rmtree(sys.argv[1])' "$eye_test_output"
}
trap cleanup_eye_tests EXIT
plugin_dir="$PWD/FCP-BeautyGirl/FCP-BeautyGirl/Plugin"
xcrun clang -fobjc-arc -fmodules -Wall -Wextra -Wno-unused-parameter \
-DCI_SILENCE_GL_DEPRECATION -mmacosx-version-min=13.0 -I "$plugin_dir" \
-framework Foundation -framework CoreImage -framework CoreGraphics \
-framework CoreMedia -framework Metal -framework Vision -framework ImageIO \
"$plugin_dir/PGFaceProcessor.m" tests/eyes_tests.m -o "$eye_test_output/eye-tests"
"$eye_test_output/eye-tests" "$@"
+12
查看文件
@@ -0,0 +1,12 @@
#!/bin/zsh
set -euo pipefail
cd "${0:A:h:h}"
output_dir=$(mktemp -d /tmp/fcp-beautygirl-tracking-tests.XXXXXX)
trap 'rm -rf "$output_dir"' EXIT
# tracking_tests.m imports the implementation; do not link it a second time.
xcrun clang -fobjc-arc -fmodules -Wall -Wextra -Wno-unused-parameter \
-DCI_SILENCE_GL_DEPRECATION -mmacosx-version-min=13.0 \
-framework Foundation -framework CoreImage -framework CoreGraphics \
-framework CoreMedia -framework Metal -framework Vision -framework ImageIO \
tests/tracking_tests.m -o "$output_dir/tracking-tests"
"$output_dir/tracking-tests"
+2
查看文件
@@ -11,3 +11,5 @@ xcrun clang "${common[@]}" "$plugin_dir/PGFaceProcessor.m" tests/face_tests.m -o
"$output_dir/face-tests" "$PWD/tests/fixtures/astronaut.png"
xcrun clang "${common[@]}" "$plugin_dir/PGFaceProcessor.m" tests/slim_tests.m -o "$output_dir/slim-tests"
"$output_dir/slim-tests" "$PWD/tests/fixtures/astronaut.png"
zsh scripts/test-tracking.sh
zsh scripts/test-eyes.sh
+4 -3
查看文件
@@ -9,13 +9,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')
parser.add_argument('--source-app',default=PRODUCT['installationPath'],help='Signed/stapled APP to compare byte for byte against the payload; may be the prepared delivery staging APP.')
args=parser.parse_args()
expanded=root/args.expanded/'Payload.pkg';payload=expanded/'Payload'
formal=Path('/Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app');packed=payload/'Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app'
formal=Path(args.source_app);packed=payload/'Library/Plug-Ins/FxPlug/FCP-BeautyGirl.app'
sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest()
def files(p):
return {str(x.relative_to(p)):('symlink:'+os.readlink(x) if x.is_symlink() else sha(x)) for x in p.rglob('*') if x.is_file() or x.is_symlink()}
assert files(formal)==files(packed),'APP differs from signed installed product'
assert files(formal)==files(packed),'APP differs from the verified signed source product'
subprocess.run(['codesign','--verify','--deep','--strict',str(packed)],check=True)
subprocess.run(['xcrun','stapler','validate',str(packed)],check=True,capture_output=True)
source=root/'Templates/大鱼人像美化';template=next(payload.rglob('大鱼人像美化.moef')).parent
@@ -73,7 +74,7 @@ assert backups and all('/Movies/' not in str(x) for x in backups)
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.'}
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' if formal==Path(PRODUCT['installationPath']) else 'Prepared signed APP verified; formal APP installation pending host exit')+'; 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.'}
(root/('docs/verification/v'+args.version+'/pkg-verification.json')).write_text(json.dumps(report,ensure_ascii=False,indent=2)+'\n');print(json.dumps(report,ensure_ascii=False,indent=2))
# This fixture belongs solely to this verifier; remove its locks before cleanup.
subprocess.run(['chflags','-R','nouchg',str(base)],check=True);shutil.rmtree(base)