← Commit history

1.0.58: Linux build in the same zip (wiki #127/#128: Flatpak KiCad, kicad-cli verbs, IPC live verbs; window verbs not yet). kicad_silk_fields_batch updates the parent footprints when KiCad refuses a field update (#129). Drill files with every Gerber export, kicad_export_pos (JLCPCB CPL), kicad_render_board, board-area SVG with background. Fixes #120 (read-only verbs leave dialogs open), #121 (Zoom to Selected Objects hint, ambiguous menu labels refused), #122 (same-name boards), #123 (PCM build-a-repository docs), #124 (DRC snapshot keeps project libraries), #125 (courtyard check uses real outlines).

John Lauer ·f01172d00a ·25d ago ·parent 14b00c3
1 file changed +32−4
tools/build_native_release.py+32−4
@@ -29,7 +29,7 @@ TARGET = "x86_64-pc-windows-gnu" PLATFORMS = {     "windows": {"supported": True, "notes": "Pre-built x64 kicad-bridge.exe ships in the zip."},     "macos": {"supported": False, "notes": "Kyle's build: cargo build --release on a Mac, crates/kicad-platform/src/macos.rs."},-    "linux": {"supported": False, "notes": "Barrett's build: cargo build --release on Ubuntu, crates/kicad-platform/src/linux.rs."},+    "linux": {"supported": True, "notes": "x86_64 kicad-bridge (glibc 2.39+: Ubuntu 24.04, Debian 13 and newer) ships in the same zip. kicad-cli verbs, file edits and the KiCad IPC API work; window verbs (screenshots, keys, menus) answer not_supported_on_platform for now. KiCad from the distro, snap or Flatpak (kicad_upgrade installs the Flathub app with --user). Wiki #127."}, }  def authenticode_size(path):@@ -94,6 +94,23 @@ def main():     if publish and not re.fullmatch(r"\d+\.\d+\.\d+", ver):         sys.exit(f"REFUSING: {ver} is not a plain x.y.z; set [workspace.package].version in rust/Cargo.toml before publishing (ab and the tiers want one plain semver per build)")     print(f"native kicad bridge {ver}")+    dist = RUST / "dist"+    zpath = dist / f"adom-bridge-kicad-native-v{ver}.zip"+    if "--reuse-zip" in sys.argv:+        # Ship the exact zip that was staged and verified on a desktop; never a rebuild of it.+        if not zpath.is_file():+            sys.exit(f"REFUSING: --reuse-zip but {zpath} does not exist")+        with zipfile.ZipFile(zpath) as z:+            verbs = json.loads(z.read("bridge.json"))["verbs"]+            inner = dist / "stage-check.exe"; inner.write_bytes(z.read("kicad-bridge.exe"))+            if "kicad-bridge" not in z.namelist():+                sys.exit("REFUSING: the zip has no Linux binary")+        ok = authenticode_size(inner) > 0; inner.unlink()+        if publish and not ok:+            sys.exit("REFUSING: the exe inside the reused zip is unsigned")+        sha = hashlib.sha256(zpath.read_bytes()).hexdigest()+        print(f"reusing {zpath.name} ({len(verbs)} verbs, sha256 {sha})")+        return publish_zip(ver, zpath, sha, notes) if publish else None     sh(["cargo", "build", "--release"], cwd=RUST)     sh(["cargo", "build", "--release", "--target", TARGET], cwd=RUST)     exe = RUST / "target" / TARGET / "release" / "kicad-bridge.exe"@@ -104,7 +121,8 @@ def main():     py = json.loads((ROOT / "bridge.json").read_text())     manifest = dict(py)     manifest["version"] = ver-    manifest["spawn"] = {"kind": "exe", "entrypoint": "kicad-bridge.exe", "port": 0, "healthEndpoint": "/status", "stopMethod": "kill", "killImageName": "kicad-bridge.exe"}+    # One zip, both binaries (wiki #127): ab 2.1.175+ picks this OS's spawn.entrypoints value.+    manifest["spawn"] = {"kind": "exe", "entrypoint": "kicad-bridge.exe", "entrypoints": {"windows": "kicad-bridge.exe", "linux": "kicad-bridge"}, "port": 0, "healthEndpoint": "/status", "stopMethod": "kill", "killImageName": "kicad-bridge.exe"}     manifest.pop("dependencies", None)     manifest["verbs"] = verbs     manifest["runtime"] = "native"@@ -112,6 +130,9 @@ def main():     dist = RUST / "dist"; dist.mkdir(exist_ok=True)     stage = dist / "stage"; shutil.rmtree(stage, ignore_errors=True); stage.mkdir()     shutil.copy2(exe, stage / "kicad-bridge.exe")+    linux_bin = RUST / "target" / "release" / "kicad-bridge"+    shutil.copy2(linux_bin, stage / "kicad-bridge")+    os.chmod(stage / "kicad-bridge", 0o755)     # SIGNING, the last change to the exe (anything after it would break the signature). Windows 11     # Smart App Control blocks unsigned exes, so a published build is ALWAYS signed as Adom     # Industries, Inc.; the signer is a private tool outside this public repo (ADOM_SIGN_EXE,@@ -156,10 +177,14 @@ def main():         if not ok:             sys.exit("REFUSING: the exe inside the zip is unsigned; nothing was published")     print(f"exe signed: {'yes (Adom Industries, Inc.)' if signed else 'NO (local build only; --publish signs)'}")-    print(f"built {zpath.name}\n  verbs : {len(verbs)}\n  exe   : {exe.stat().st_size} bytes\n  zip   : {zpath.stat().st_size} bytes\n  sha256: {sha}")+    print(f"built {zpath.name}\n  verbs : {len(verbs)}\n  linux : {linux_bin.stat().st_size} bytes\n  exe   : {exe.stat().st_size} bytes\n  zip   : {zpath.stat().st_size} bytes\n  sha256: {sha}")     if not publish:         print("not published (pass --publish --notes \"...\" to ship to insiders)")         return+    publish_zip(ver, zpath, sha, notes)+++def publish_zip(ver, zpath, sha, notes):     from datetime import datetime, timezone     REF = "adom/kicad-bridge"     # 1. The repo must tell the same story as the zip: push every rust source file.@@ -178,7 +203,10 @@ def main():     m.update(version=ver, sha256=sha, size=zpath.stat().st_size,              url=f"https://wiki.adom.inc/download/{REF}/{ver}/{zpath.name}",              released_at=datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),-             runtime="native", platforms=PLATFORMS)+             runtime="native")+    # ab reads every platforms entry as a download (url, sha256, size): one zip serves both OSes.+    dl = {"url": m["url"], "sha256": sha, "size": zpath.stat().st_size}+    m["platforms"] = {k: dict(v, **dl) for k, v in PLATFORMS.items() if v.get("supported")}     mpath.write_text(json.dumps(m, indent=2) + "\n")     # 4. INSIDERS tier only. The public manifest moves through tools/promote.py.     ins = dict(m); ins["tier"] = "insiders"