FrameBridge 0.3.1 community source release

This commit is contained in:
FrameBridge Development committed 2026-10-26 22:09:18 -04:00
commit b23819fff1
1354 files changed
+343716

No files matched your search

+115
View File
@@ -0,0 +1,115 @@
#!/usr/bin/python3
"""Bounded static and loopback HTTP checks for the FrameBridge website."""
from pathlib import Path
from html.parser import HTMLParser
from urllib.parse import urlsplit,unquote
from urllib.request import urlopen
from http.server import ThreadingHTTPServer,SimpleHTTPRequestHandler
import json,hashlib,threading,tempfile,shutil,subprocess,html,os
BASE=Path(__file__).resolve().parent
DIST=BASE/'dist'
REPORT=BASE/'qa/WEBSITE-STATUS.json'
REPORT.parent.mkdir(mode=0o700,exist_ok=True)
class Inspect(HTMLParser):
def __init__(self):
super().__init__();self.links=[];self.assets=[];self.ids=set();self.lang=None;self.main=False;self.scripts=[];self.code=[];self.in_code=False;self.current=''
def handle_starttag(self,tag,attrs):
a=dict(attrs)
if tag=='html':self.lang=a.get('lang')
if 'id' in a:self.ids.add(a['id'])
if tag=='main' and a.get('id')=='content':self.main=True
if tag=='a' and 'href'in a:self.links.append(a['href'])
if tag in ('script','source','img') and 'src'in a:self.assets.append(a['src'])
if tag=='video' and 'poster'in a:self.assets.append(a['poster'])
if tag=='link' and a.get('rel') in ('stylesheet','icon'):self.assets.append(a['href'])
if tag=='script':self.scripts.append(a)
if tag=='code':self.in_code=True;self.current=''
def handle_data(self,data):
if self.in_code:self.current+=data
def handle_endtag(self,tag):
if tag=='code':self.code.append(self.current);self.in_code=False
def resolve(path):
p=DIST/unquote(path.lstrip('/'))
return p/'index.html' if path.endswith('/') or p.is_dir() else p
def main():
os.umask(0o077);pages=sorted(DIST.rglob('*.html'));assert pages
failures=[];pending=[];infra=set();http_paths=set();external=set()
for p in pages:
raw=p.read_text();q=Inspect();q.feed(raw);rel='/'+str(p.relative_to(DIST));http_paths.add(rel)
if q.lang!='en' or not q.main:failures.append(str(p.relative_to(DIST))+': missing English or main landmark')
if 'gerabh' in raw or '/home/gerabh' in raw:failures.append(str(p.relative_to(DIST))+': personal host path')
if "'unsafe-inline'" in raw:failures.append(str(p.relative_to(DIST))+': unsafe inline CSP')
for value in q.links+q.assets:
u=urlsplit(value)
if u.scheme or u.netloc:
external.add(value);continue
if not u.path:
if u.fragment and u.fragment not in q.ids:failures.append(str(p.relative_to(DIST))+': missing anchor '+u.fragment)
continue
if any(u.path.startswith(prefix) for prefix in ['/git/','/debian/','/downloads/']):
infra.add(u.path);continue
if not u.path.startswith('/'):failures.append('Non-root relative URL '+value);continue
target=resolve(u.path)
if not target.exists():
if u.path=='/assets/framebridge-preview.mp4':pending.append('Preview video asset pending')
else:failures.append('Missing local target '+value+' referenced by '+str(p.relative_to(DIST)))
elif u.path in q.assets:http_paths.add(u.path)
if u.fragment and target.is_file() and target.suffix=='.html':
other=Inspect();other.feed(target.read_text())
if u.fragment not in other.ids:failures.append('Missing target fragment '+value)
for item in q.scripts:
if item.get('src')!='/assets/site.js':failures.append('Unexpected script source')
css=(DIST/'assets/site.css').read_text()
assert css.count('{')==css.count('}'),'CSS brace mismatch'
class Quiet(SimpleHTTPRequestHandler):
def __init__(self,*a,**k):super().__init__(*a,directory=str(DIST),**k)
def log_message(self,*a):pass
server=ThreadingHTTPServer(('127.0.0.1',0),Quiet);thread=threading.Thread(target=server.serve_forever,daemon=True);thread.start()
http_count=0
try:
for path in sorted(http_paths):
with urlopen('http://127.0.0.1:'+str(server.server_port)+path,timeout=3) as response:
if response.status!=200:failures.append('HTTP status '+str(response.status)+' for '+path)
response.read();http_count+=1
finally:server.shutdown();server.server_close();thread.join(timeout=2)
# Exercise the guarded final-metadata branch using clearly synthetic values
# in a disposable directory. No signature or installation command executes.
meta=json.loads((BASE/'release.json').read_text())
with tempfile.TemporaryDirectory(prefix='framebridge-web-qa-') as td:
temp=Path(td);web=temp/'web';web.mkdir()
shutil.copyfile(BASE/'build_site.py',web/'build_site.py')
shutil.copytree(BASE/'src',web/'src')
(web/'assets').mkdir()
for p in (BASE/'assets').iterdir():
if p.is_file() and p.suffix!='.mp4':shutil.copyfile(p,web/'assets'/p.name)
fixture=dict(meta,state='ready',sha256='a'*64,signing_fingerprint='A'*40,size_bytes=1,source_archive_url='/downloads/SYNTHETIC-SOURCE.tar.gz')
(web/'release.json').write_text(json.dumps(fixture))
result=subprocess.run(['/usr/bin/python3','-I','-S',str(web/'build_site.py')],capture_output=True,text=True,timeout=10)
if result.returncode:failures.append('Synthetic-ready rendering failed: '+result.stderr[-600:])
else:
q=Inspect();q.feed((web/'dist/download/index.html').read_text())
blocks=[x for x in q.code if '\n' in x]
if len(blocks)!=3:failures.append('Expected manual verification, APT setup and after-install command blocks')
for text in blocks:
result=subprocess.run(['/bin/bash','-n'],input=text,text=True,capture_output=True,timeout=3)
if result.returncode:failures.append('Rendered install block shell syntax: '+result.stderr.strip())
manual=next((x for x in blocks if 'gpgv --keyring' in x),'')
if not manual or 'sha256sum --check' not in manual or 'expected="' not in manual:failures.append('Manual flow missing required verification')
apt=next((x for x in blocks if 'sources.list.d' in x),'')
if 'signed-by=' not in apt or 'apt-key' in apt:failures.append('APT trust not scoped with Signed-By')
status={'host':'GERAMEG','website_source':str(BASE),'web_root':str(DIST),'language':'English','buildless':True,'html_pages_checked':len(pages),'loopback_http_paths_checked':http_count,'static_failures':failures,'pending_assets':sorted(set(pending)),'infra_routes_expected':sorted(infra),'external_links':sorted(external),'no_external_runtime_dependencies':True,'copy_controls_progressive':True,'ready_branch_rendered_with_synthetic_fixture':True,'install_blocks_syntax_checked_not_executed':True,'release_state':meta.get('state'),'package_sha256_present':bool(meta.get('sha256')),'signing_fingerprint_present':bool(meta.get('signing_fingerprint')),'video_present':(DIST/'assets/framebridge-preview.mp4').is_file(),'visual_browser_QA_performed':False,'visual_browser_QA_limit':'No browser or user desktop was operated by this subtask. Root may inspect the staged site via the authorized GERAMEG surface.','production_network_or_services_changed':False,'site_ready_for_private_staging':not failures,'public_release_ready':not failures and not pending and meta.get('state')=='ready','homepage_sha256':hashlib.sha256((DIST/'index.html').read_bytes()).hexdigest()}
visual=BASE/'qa/VISUAL-REVIEW.json'
if visual.exists():
status['visual_browser_QA_performed']=True
status['visual_browser_QA_limit']=json.loads(visual.read_text())['limitations']
status['visual_browser_QA_report']=str(visual)
status.update(support_page='/support/',support_funding_channel_configured=bool(meta.get('support_payment_url')),support_payment_form_present='<form' in (DIST/'support/index.html').read_text(),license_identifier=meta.get('license'),dependency_source_archives=len(meta.get('dependency_sources',[])))
video=DIST/'assets/framebridge-preview.mp4'
if video.exists():
status['video_sha256']=hashlib.sha256(video.read_bytes()).hexdigest()
guide=json.loads((DIST/'assets/command-guide.json').read_text())
status.update(shared_guide_version=guide['version'],shared_guide_topics=len(guide['topics']),shared_guide_sha256=guide['guide_sha256'])
REPORT.write_text(json.dumps(status,indent=2)+'\n')
print(json.dumps({'pages':len(pages),'http_paths':http_count,'failures':failures,'pending_assets':status['pending_assets'],'private_staging_ready':status['site_ready_for_private_staging'],'public_ready':status['public_release_ready'],'report':str(REPORT)},indent=2))
return 1 if failures else 0
if __name__=='__main__':raise SystemExit(main())