app
KiCad - the KiCad Bridge
Public Made by Adomby adom
Reference implementation of the KiCad bridge: multi-instance Python server, forward path via kicad-cli, reverse path via in-process plugin. Most complex of the three bundled bridges.
← Commit history
tools/build_release.pyadded+94@@ -0,0 +1,94 @@+#!/usr/bin/env python3+"""Build (and optionally publish) a kicad-bridge runtime release.++Until now the shipping file list lived only in a maintainer's scratchpad, so+nobody else could reproduce a release and a container reset would have lost it.+It is `tools/release_files.json` now, in the repo, next to this script.++ python3 tools/build_release.py 0.9.219 # build + update manifest+ python3 tools/build_release.py 0.9.219 --publish # ... and push + upload++The zip is built from the explicit list, never a directory walk: a stray file in+the working tree must never end up on a user's machine.+"""+import hashlib+import json+import os+import subprocess+import sys+import zipfile+from datetime import datetime, timezone++ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))+REF = "adom/kicad-bridge"+MANIFEST = "adom-bridge-kicad-manifest.json"+++def main() -> int:+ if len(sys.argv) < 2:+ print(__doc__)+ return 2+ version = sys.argv[1].lstrip("v")+ publish = "--publish" in sys.argv+ os.chdir(ROOT)++ files = json.load(open("tools/release_files.json"))["files"]+ missing = [f for f in files if not os.path.isfile(f)]+ if missing:+ print("REFUSING TO BUILD - listed files are missing:")+ for m in missing:+ print(" ", m)+ return 1++ # Version lives in two places and they must agree; a mismatch has shipped+ # before and is invisible until someone reads bridgeVersion off a box.+ open("BRIDGE_VERSION", "w").write(version)+ bj = json.load(open("bridge.json"))+ bj["version"] = version+ json.dump(bj, open("bridge.json", "w"), indent=2)++ out = f"adom-bridge-kicad-v{version}.zip"+ with zipfile.ZipFile(out, "w", zipfile.ZIP_DEFLATED) as z:+ for f in files:+ z.write(f, f)++ raw = open(out, "rb").read()+ sha, size = hashlib.sha256(raw).hexdigest(), len(raw)++ # Guards for two failures that actually shipped: a raw NUL byte written into+ # a source file (un-importable, bricks the bridge), and a version mismatch+ # between the zip and BRIDGE_VERSION.+ with zipfile.ZipFile(out) as z:+ if z.read("BRIDGE_VERSION").decode().strip() != version:+ print("REFUSING: BRIDGE_VERSION inside the zip does not match", version)+ return 1+ for n in z.namelist():+ if n.endswith(".py") and b"\x00" in z.read(n):+ print(f"REFUSING: NUL byte in {n} - Python cannot import this file")+ return 1++ m = json.load(open(MANIFEST))+ m.update(version=version, sha256=sha, size=size,+ url=f"https://wiki.adom.inc/download/{REF}/{version}/{out}",+ released_at=datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"))+ json.dump(m, open(MANIFEST, "w"), indent=2)++ print(f"built {out}\n files : {len(files)}\n size : {size}\n sha256: {sha}")+ if not publish:+ print("\nnot published (pass --publish). Manifest updated locally.")+ return 0++ subprocess.run(["adom-wiki", "repo", "push", REF, "--files",+ "BRIDGE_VERSION", "bridge.json", MANIFEST], check=False)+ subprocess.run(["adom-wiki", "release", "create", REF, version], check=False)+ subprocess.run(["adom-wiki", "release", "upload", REF, version, out], check=False)+ print("\nPUBLISHED. Now INSTALL it - refresh_bridges does not pull a release:")+ print(f" adom-bridge --target <BOX> bridge_install '{{\"manifestUrl\":"+ f"\"https://wiki.adom.inc/api/v1/pages/kicad-bridge/files/{MANIFEST}\","+ f"\"force\":true,\"reason\":\"...\"}}'")+ print(" then confirm with kicad_status -> bridgeVersion")+ return 0+++if __name__ == "__main__":+ raise SystemExit(main())
tools/release_files.jsonadded+75@@ -0,0 +1,75 @@+{+ "_comment": "The exact runtime payload. Adding a file here ships it to every Bridge user; the release zip is built from THIS list, never from a directory walk. skills/, dev-skills/, publish-skills/, dashboard/, demo/, docs/, images and *.md except SKILL.md are deliberately absent - they are the page repo's job, not the runtime's.",+ "files": [+ "BRIDGE_VERSION",+ "LICENSE",+ "SKILL.md",+ "adom_library.py",+ "bridge.json",+ "caller_identity.py",+ "capabilities.py",+ "certs/cacert.pem",+ "dock.json",+ "handlers/__init__.py",+ "handlers/ad_client.py",+ "handlers/bom.py",+ "handlers/bridge_client.py",+ "handlers/close_windows.py",+ "handlers/demo.py",+ "handlers/diagnostics.py",+ "handlers/export.py",+ "handlers/export_molecule.py",+ "handlers/export_part.py",+ "handlers/fix_keyboard.py",+ "handlers/install_footprint.py",+ "handlers/install_library.py",+ "handlers/install_symbol.py",+ "handlers/kicad_cli_lint.py",+ "handlers/kicad_ui.py",+ "handlers/launch.py",+ "handlers/library_browser.py",+ "handlers/make_part_project.py",+ "handlers/netlist.py",+ "handlers/open_files.py",+ "handlers/open_footprint_editor.py",+ "handlers/open_symbol_editor.py",+ "handlers/place_footprint.py",+ "handlers/plugin_diagnose.py",+ "handlers/png_stdlib.py",+ "handlers/progress.py",+ "handlers/run_drc.py",+ "handlers/settings.py",+ "handlers/show.py",+ "handlers/software_opengl.py",+ "handlers/state.py",+ "handlers/uia.py",+ "handlers/uninstall.py",+ "handlers/upgrade.py",+ "handlers/win_focus.py",+ "handlers/win_menu.py",+ "handlers/win_taskbar.py",+ "install.sh",+ "kicad_detect.py",+ "launch_editor.ps1",+ "lib_table.py",+ "navigate_symbol.ps1",+ "package.json",+ "page.json",+ "parsers/__init__.py",+ "parsers/pcb.py",+ "parsers/schematic.py",+ "parsers/sexpr.py",+ "plugin_install.py",+ "plugin_payload/adom_bridge.py",+ "plugin_payload/usercustomize.py",+ "proc.py",+ "quiet_console.py",+ "resources/adom-overlay-32.png",+ "resources/adom-overlay.ico",+ "server.py",+ "start.bat",+ "start.sh",+ "templates/blank-board.kicad_pcb",+ "uninstall.sh"+ ]+}