chore: baseline V8.10.0-dev.1 with v8_workflows type-mapping fix and trade-pro spec
- SiteForge Enterprise V8.10.0-dev.1 upstream source (210 tests) - Fix: v8_workflows._mapped_input now reads both 类型/分类 columns so WXR imports keep post/product types instead of collapsing to page - Docs: trade-pro theme port spec (R3) and implementation plan Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
"""Deterministic P3 5,000-product acceptance pilot (isolated, no network).
|
||||
|
||||
Run: python scripts/pilot_5000.py --report pilot.json
|
||||
Optional full build: python scripts/pilot_5000.py --build --report pilot-build.json
|
||||
No production data are opened or modified. Uses an independent temporary SQLite
|
||||
volume, 10 x 500 rows; builds the full automotive static site and runs static QA.
|
||||
A successful test is NOT a commercial content/SEO readiness certificate.
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
ROOT=Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0,str(ROOT))
|
||||
|
||||
|
||||
def run(build=False):
|
||||
started=time.monotonic()
|
||||
with tempfile.TemporaryDirectory(prefix='siteforge-5k-pilot-') as work:
|
||||
os.environ['SITEFORGE_DATA_DIR']=work
|
||||
os.environ.setdefault('SITEFORGE_ADMIN_PASSWORD','offline-pilot-only-long-password-123456789')
|
||||
os.environ.setdefault('SITEFORGE_SESSION_SECRET','offline-pilot-only-session-1234567890123456789')
|
||||
from fastapi.testclient import TestClient
|
||||
from app.main import app
|
||||
from app import db,builder,quality,v8_workflows as wf
|
||||
from app.core import uid
|
||||
with TestClient(app):
|
||||
sid=uid();now=db.now()
|
||||
db.insert('INSERT INTO sites(id,name,theme,created_at,updated_at) VALUES(?,?,?,?,?)',
|
||||
(sid,'5K Acceptance Pilot','automotive',now,now))
|
||||
for offset in range(0,5000,500):
|
||||
data=[{'车型名称':f'Vehicle {i:05d}','品牌':f'Brand {i%20:02d}',
|
||||
'SEO描述':f'Demo vehicle {i:05d}, pilot only'}
|
||||
for i in range(offset,offset+500)]
|
||||
batch=wf.stage(sid,data,kind='vehicle',origin=f'pilot-{offset//500}',
|
||||
source_keys=[str(i) for i in range(offset,offset+500)])
|
||||
result=wf.commit(sid,batch['batch_id'])
|
||||
assert result['created']==500 and not result['errors'],result
|
||||
import_sec=round(time.monotonic()-started,2)
|
||||
assert db.one("SELECT COUNT(*) n FROM entries WHERE site_id=? AND type='vehicle'",(sid,))['n']==5000
|
||||
if not build:
|
||||
return {'status':'import_passed_build_not_run','source_rows':5000,
|
||||
'committed':5000,'batches':10,'import_seconds':import_sec,
|
||||
'note':'Use --build in a sufficiently provisioned CI runner for full static-build QA.'}
|
||||
report=builder.build(sid,False)
|
||||
build_sec=round(time.monotonic()-started-import_sec,2)
|
||||
# Release/SEO QA runs in its own short-lived process in production.
|
||||
# Do not reuse the large catalogue renderer's heap for the audit.
|
||||
audit_code=("import json,sys; from app import quality; "
|
||||
"print(json.dumps(quality.report(sys.argv[1],sys.argv[2])))")
|
||||
audit=subprocess.run([sys.executable,'-c',audit_code,sid,report['version']],
|
||||
cwd=str(ROOT),text=True,capture_output=True,timeout=120,check=True,
|
||||
env=dict(os.environ))
|
||||
qa=json.loads(audit.stdout)
|
||||
result={'status':'passed' if qa['errors']==0 and report['pages']>=5000 else 'failed',
|
||||
'source_rows':5000,'batches':10,'pages':report['pages'],'html_files':qa['files'],
|
||||
'static_errors':qa['errors'],'static_warnings':qa['warnings'],
|
||||
'import_seconds':import_sec,'build_seconds':build_sec,
|
||||
'total_seconds':round(time.monotonic()-started,2),
|
||||
'disclaimer':'Fixed sample data; not live MySQL, not Docker, not production SEO audit.'}
|
||||
if result['status']!='passed':raise AssertionError(result)
|
||||
return result
|
||||
|
||||
if __name__=='__main__':
|
||||
p=argparse.ArgumentParser();p.add_argument('--report');p.add_argument('--build',action='store_true');args=p.parse_args()
|
||||
result=run(build=args.build)
|
||||
if args.report:Path(args.report).write_text(json.dumps(result,ensure_ascii=False,indent=2))
|
||||
print(json.dumps(result,ensure_ascii=False,indent=2))
|
||||
Reference in New Issue
Block a user