Align 2.4.0 pin tracking and pupil-based eyes; add sequence and target-field regressions
这个提交包含在:
1 父节点
524f536a53
当前提交
75e7f4a00d
26 个文件已更改
+16839
-87
没有与搜索条件相匹配的文件
@@ -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"
|
||||
@@ -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
|
||||
@@ -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)
|
||||
在新工单中引用
屏蔽一个用户