AnasSarkiz/magnetic-shutter-remote-r8

A USB‑C rechargeable battery-powered ESP32‑C3 wireless controller with Li‑ion charging, protected 3.3‑V buck‑boost regulation, thermal/safety monitoring, status LEDs, buttons, programming UART, crystal clock, and 2.4‑GHz antenna matching network.

Version
0.4.0-prototype
License
unset
Stars
0

scripts/package-r9-native-exports.py

"""Package official native exports unchanged; retain exact input/file provenance."""
import argparse
import hashlib
import json
from pathlib import Path
from zipfile import ZipFile

NATIVE_FILES = {'F_Cu.gbr','F_SilkScreen.gbr','F_Mask.gbr','F_Paste.gbr',
                'B_Cu.gbr','B_SilkScreen.gbr','B_Mask.gbr','B_Paste.gbr',
                'Edge_Cuts.gbr','F_Fab.gbr','drill-L1-L2.drl','drill_npth.drl'}


def package(options):
    circuit_bytes=options.circuit.read_bytes()
    circuit=json.loads(circuit_bytes)
    if any(e['type'].endswith('_error') for e in circuit) or not any(e['type']=='pcb_trace' for e in circuit):
        raise ValueError('Refusing failed or unrouted native circuit')
    options.directory.mkdir(parents=True,exist_ok=True)
    if set(p.name for p in options.directory.iterdir())-NATIVE_FILES:
        raise ValueError('Unexpected existing file in native export directory; preserve and review it')
    files=[]
    with ZipFile(options.archive) as archive:
        if set(archive.namelist())!=NATIVE_FILES|{'bom.csv','pick_and_place.csv'}:
            raise ValueError('Official export file coverage differs')
        for name in sorted(NATIVE_FILES):
            raw=archive.read(name);target=options.directory/name;target.write_bytes(raw)
            if target.read_bytes()!=raw:raise ValueError('Native file bytes changed')
            files.append({'path':str(target),'sha256':hashlib.sha256(raw).hexdigest(),'bytes':len(raw),'matches_native_cli_bytes':True})
    report={'source_circuit_sha256':hashlib.sha256(circuit_bytes).hexdigest(),
            'official_cli_zip_sha256':hashlib.sha256(options.archive.read_bytes()).hexdigest(),
            'native_fabrication_files':files,
            'assembly_files':'Use separately audited JLCPCB-BOM.csv/JLCPCB-CPL.csv, not default CLI CSVs',
            'manufacturing_acceptance':'Independent CAM review required; packaging is not a release gate pass'}
    options.report.write_text(json.dumps(report,indent=2)+'\n')
    print('12 exact native files retained; no Circuit JSON/Gerber geometry edited')


if __name__=='__main__':
    parser=argparse.ArgumentParser()
    parser.add_argument('archive',type=Path);parser.add_argument('circuit',type=Path)
    parser.add_argument('--directory',type=Path,required=True);parser.add_argument('--report',type=Path,required=True)
    package(parser.parse_args())