← Commit history

native release: --publish ships to the insiders tier the way build_release.py does

John Lauer ·31bf8c3984 ·27d ago ·parent e3d8089
1 file changed +73−3
tools/build_native_release.py+73−3
@@ -1,7 +1,15 @@ #!/usr/bin/env python3-"""Build the native (Rust) kicad bridge zip for Windows. Does NOT publish.+"""Build the native (Rust) kicad bridge zip for Windows, and optionally ship it to INSIDERS. -  python3 tools/build_native_release.py            # builds, zips, prints sha256+  python3 tools/build_native_release.py                                  # build only+  python3 tools/build_native_release.py --publish --notes "what changed"  # insiders tier++Publishing mirrors tools/build_release.py: push every shipped source file to the page repo+(the rust/ tree), create the release on adom/kicad-bridge and upload the zip, update the+local manifest (version, url, sha256, size), push the INSIDERS manifest to adom/insiders+(the public tier moves only through tools/promote.py with a human's words), and record the+note in RELEASE_NOTES.md. The version must be a plain x.y.z (no prerelease suffix) to+publish; alpha builds are for staging on a pinned box only.  Output: rust/dist/adom-bridge-kicad-native-v<ver>.zip containing kicad-bridge.exe, bridge.json (spawn.kind exe, verbs generated from the binary's own kicad_describe) and@@ -43,8 +51,30 @@ def describe_via_linux_binary():     finally:         p.kill() +def push_chunked(ref, files, msg, cwd=None):+    """Push files to a page repo in chunks; every reply checked (issue #79)."""+    failed = []+    for i in range(0, len(files), 40):+        chunk = files[i:i + 40]+        proc = subprocess.run(["adom-wiki", "repo", "push", ref, "-m", msg, "--files"] + chunk, capture_output=True, text=True, cwd=cwd)+        ok = '"ok": true' in proc.stdout+        print(f"  push {i // 40 + 1}: {len(chunk)} file(s) -> {'ok' if ok else 'FAILED'}")+        if not ok:+            print((proc.stdout + proc.stderr)[-600:])+            failed.extend(chunk)+    return failed++ def main():+    publish = "--publish" in sys.argv+    notes = ""+    if "--notes" in sys.argv:+        notes = sys.argv[sys.argv.index("--notes") + 1]+    if publish and not notes.strip():+        sys.exit("REFUSING: --publish needs --notes \"what changed\" (the page reuses it as the changelog)")     ver = version()+    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}")     sh(["cargo", "build", "--release"], cwd=RUST)     sh(["cargo", "build", "--release", "--target", TARGET], cwd=RUST)@@ -90,7 +120,47 @@ def main():                 z.write(f, f.relative_to(stage).as_posix())     sha = hashlib.sha256(zpath.read_bytes()).hexdigest()     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("not published: flip the manifest by hand once the verb runner passes on the native binary")+    if not publish:+        print("not published (pass --publish --notes \"...\" to ship to insiders)")+        return+    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.+    src = [str(p.relative_to(ROOT)) for p in sorted((RUST).rglob("*")) if p.is_file() and "target" not in p.parts and "dist" not in p.parts]+    src += ["tools/build_native_release.py"]+    msg = f"{ver}: {notes}"+    failed = push_chunked(REF, src, msg, cwd=str(ROOT))+    if failed:+        sys.exit(f"REFUSING TO RELEASE: source push failed for {len(failed)} file(s)")+    # 2. Release row + zip on the product page (never on adom/insiders: adom/wiki#170).+    subprocess.run(["adom-wiki", "release", "create", REF, ver, "--changelog", notes], check=False)+    subprocess.run(["adom-wiki", "release", "upload", REF, ver, str(zpath)], check=False)+    # 3. Manifest: same document shape as the Python bridge's, pointing at this zip.+    mpath = ROOT / "adom-bridge-kicad-manifest.json"+    m = json.loads(mpath.read_text())+    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")+    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"+    import tempfile+    d = Path(tempfile.mkdtemp(prefix="kicad-native-insiders-"))+    (d / "kicad-bridge").mkdir()+    (d / "kicad-bridge" / "version.json").write_text(json.dumps(ins, indent=2) + "\n")+    r = subprocess.run(["adom-wiki", "repo", "push", "adom/insiders", "-m", f"kicad-bridge {ver} (native): {notes}", "--files", "kicad-bridge/version.json"], cwd=str(d), capture_output=True, text=True)+    if '"ok": true' not in r.stdout:+        sys.exit("REFUSING: the insiders manifest push failed:\n" + (r.stdout + r.stderr)[-600:])+    # 5. Ledger.+    led = ROOT / "RELEASE_NOTES.md"+    entry = f"## {ver}  ({datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M UTC')})\n\nNATIVE (Rust) build. {notes}\n\n"+    prior = led.read_text(encoding="utf-8")+    head, _, rest = prior.partition("\n\n## ")+    led.write_text((head + "\n\n" + entry + ("## " + rest if rest else "")) if rest else (prior + entry), encoding="utf-8")+    print(f"\nSHIPPED TO INSIDERS ({ver}, native). Public tier untouched.")+    print(f"  Promote with: python3 tools/promote.py {ver} --because \"<your words>\"")+    print("  Boxes pick it up on the 4 h poll, or now with bridge_install {manifestUrl: the insiders version.json, force: true}")  if __name__ == "__main__":     main()