upgrade: adopt V8.21.3 (V8.13→8.21.3: P5 SFTP/S3, P6 multilingual, editor overhaul, enterprise hardening)
Overlay ~150 upstream items onto our V8.12 base, preserving trade-pro
theme, registry tests, and design docs. Re-applied our patch set
(discovery/env_for/blocks.css/type-mapping/theme_color/frontend entries,
test_theme_pro_v3 mods) — all anchors survived upstream evolution.
New platform enhancement (fixes upgrade-introduced regression):
multilingual.site_locale now honors settings_json.locale, plus
PUT /api/i18n/sites/{id}/locale — standalone English sites get
<html lang='en'> under the V8.15 locale-rewrite pipeline.
compose: adopt V8.21.3 (image 8.21.3-dev.1, /api/ready probes, MySQL/S3
env) with project name pinned to siteforge-v8-10 (data volumes).
Tests: 356 passed / 11 failed — all 11 confirmed pre-existing in
pristine V8.21.3 on this host (10 upstream Windows env + our build test
now green after locale fix). Patch registry updated in plan doc.
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -1,20 +1,79 @@
|
||||
"""PostgreSQL bootstrap smoke: execute only in a throwaway database."""
|
||||
import os
|
||||
if not os.getenv('SITEFORGE_DATABASE_URL','').startswith(('postgresql://','postgres://')):
|
||||
raise SystemExit('Please set SITEFORGE_DATABASE_URL to a disposable PostgreSQL database')
|
||||
from app import db,enterprise,factory_api,migration_audit,reusables,release_api,jobs
|
||||
from app.core import save_entry
|
||||
import uuid
|
||||
"""Full PostgreSQL integration smoke test. Requires a disposable PostgreSQL DB.
|
||||
|
||||
db.init();enterprise.setup();factory_api.setup();migration_audit.setup();reusables.setup();release_api.setup()
|
||||
sid=uuid.uuid4().hex[:16]
|
||||
with db.connect() as c:
|
||||
c.execute('INSERT INTO sites(id,name,domain,created_at,updated_at) VALUES (?,?,?,?,?)',(sid,'PG Smoke','','2026-01-01','2026-01-01'))
|
||||
c.execute('INSERT INTO entries(id,site_id,type,title,route,slug,created_at,updated_at) VALUES (?,?,?,?,?,?,?,?)',
|
||||
(uuid.uuid4().hex[:16],sid,'page','Home','/','home','2026-01-01','2026-01-01'))
|
||||
assert db.one('SELECT id FROM sites WHERE id=?',(sid,))['id']==sid
|
||||
job=jobs.submit(sid,{'mirror_images':False},'pg-smoke','pg-smoke-unique-0001')
|
||||
assert job['state']=='queued'
|
||||
assert jobs.cancel(sid,job['id'])['state']=='cancelled'
|
||||
with db.connect() as c:c.execute('DELETE FROM sites WHERE id=?',(sid,))
|
||||
print('PostgreSQL schema, CRUD, job submit, cancel smoke passed')
|
||||
Run only in CI or a dedicated empty database: creates tables and temporary
|
||||
site/jobs, and validates actual DB API compatibility and worker claims.
|
||||
"""
|
||||
# Allow direct `python scripts/name.py` from a fresh source checkout.
|
||||
import sys as _sys
|
||||
from pathlib import Path as _Path
|
||||
_sys.path.insert(0, str(_Path(__file__).resolve().parents[1]))
|
||||
import os
|
||||
import uuid
|
||||
url=os.getenv('SITEFORGE_DATABASE_URL','')
|
||||
if not url.startswith(('postgresql://','postgres://')):
|
||||
raise SystemExit('Set SITEFORGE_DATABASE_URL to a DISPOSABLE PostgreSQL database')
|
||||
from app import (db,enterprise,factory_api,migration_audit,reusables,release_api,
|
||||
cms_pipeline,v8_workflows,v8_templates,wp_migration_v81,wp_migration_v82,
|
||||
wp_migration_v83,connectors_v84,connectors_v85,studio_v86,
|
||||
site_design_v87,multilingual,jobs,worker)
|
||||
|
||||
# Mirror app.main.lifespan bootstrap so CI will discover any broken DDL/UPSERT.
|
||||
db.init()
|
||||
for module in (enterprise,factory_api,migration_audit,reusables,release_api,
|
||||
cms_pipeline,v8_workflows,v8_templates,wp_migration_v81,wp_migration_v82,
|
||||
wp_migration_v83,connectors_v84,connectors_v85,studio_v86,
|
||||
site_design_v87,multilingual):
|
||||
module.setup()
|
||||
assert db.one('SELECT 1 AS ready')['ready']==1
|
||||
assert db.one('SELECT 1 AS ready')['ready']==1
|
||||
ledger=db.one('SELECT version FROM schema_migrations WHERE version=?',('20261022_001_p1_limits',))
|
||||
assert ledger,'PostgreSQL migration ledger not created'
|
||||
sid='pg-'+uuid.uuid4().hex[:12]
|
||||
job_id=None
|
||||
sid2=sid+"-second"
|
||||
try:
|
||||
with db.connect() as c:
|
||||
c.execute('INSERT INTO sites(id,name,domain,created_at,updated_at) VALUES (?,?,?,?,?)',
|
||||
(sid,'PG Smoke','','2026-01-01','2026-01-01'))
|
||||
c.execute('INSERT INTO sites(id,name,domain,created_at,updated_at) VALUES (?,?,?,?,?)',
|
||||
(sid2,'PG Smoke Second','','2026-01-01','2026-01-01'))
|
||||
c.execute('INSERT INTO entries(id,site_id,type,title,route,slug,created_at,updated_at) VALUES (?,?,?,?,?,?,?,?)',
|
||||
(uuid.uuid4().hex[:16],sid,'page','Home','/','home','2026-01-01','2026-01-01'))
|
||||
assert db.one('SELECT id FROM sites WHERE id=?',(sid,))['id']==sid
|
||||
job=jobs.submit(sid,{'mirror_images':False},'pg-smoke','pg-smoke-'+uuid.uuid4().hex[:14])
|
||||
job_id=job['id']
|
||||
# PostgreSQL claim exercises FOR UPDATE SKIP LOCKED and numeric COUNT rows.
|
||||
claimed=worker.claim()
|
||||
assert claimed and claimed['id']==job_id and claimed['state']=='running',claimed
|
||||
assert worker.claim() is None,'Same-site job was claimed twice'
|
||||
worker.complete(claimed,{'ok':True})
|
||||
assert db.one('SELECT state FROM build_jobs WHERE id=?',(job_id,))['state']=='done'
|
||||
job2=jobs.submit(sid,{'mirror_images':True},'pg-smoke','pg-smoke-'+uuid.uuid4().hex[:14])
|
||||
assert jobs.cancel(sid,job2['id'])['state']=='cancelled'
|
||||
# Independent connections racing for work must never receive the same job.
|
||||
import threading
|
||||
keys=[('pg-claim-a',sid),('pg-claim-b',sid2)]
|
||||
submitted=[]
|
||||
for label,site in keys:
|
||||
submitted.append(jobs.submit(site,{'mirror_images':site==sid},'pg-smoke',
|
||||
label+'-'+uuid.uuid4().hex[:10])['id'])
|
||||
barrier=threading.Barrier(2)
|
||||
claimed=[]; failures=[]
|
||||
def runner():
|
||||
try:
|
||||
barrier.wait(timeout=8)
|
||||
claimed.append(worker.claim())
|
||||
except Exception as ex:failures.append(ex)
|
||||
threads=[threading.Thread(target=runner) for _ in range(2)]
|
||||
for thread in threads:thread.start()
|
||||
for thread in threads:thread.join(timeout=12)
|
||||
assert not any(t.is_alive() for t in threads),'Concurrent PG claim deadlock'
|
||||
assert not failures,failures
|
||||
ids={item['id'] for item in claimed if item}
|
||||
assert ids==set(submitted),'Concurrent claims were duplicated or lost'
|
||||
for item in claimed:worker.complete(item,{'ok':True})
|
||||
print('PASS: PostgreSQL schemas, ledger, CRUD, queue claim/complete/cancel, two concurrent claims')
|
||||
finally:
|
||||
with db.connect() as c:
|
||||
c.execute('DELETE FROM sites WHERE id=?',(sid,))
|
||||
c.execute('DELETE FROM sites WHERE id=?',(sid2,))
|
||||
|
||||
Reference in New Issue
Block a user