← Commit history

Publish 1.9.175

John Lauer ·e3458563c4 ·1mo ago ·parent f2d405d
2 files changed +164−10
page.json+7−10
@@ -11,10 +11,6 @@     "uninstall": "./uninstall.sh"   },   "tags": [-    "ab",-    "bridge",-    "fusion",-    "desktop-app",     "fusion360",     "autodesk",     "cad",@@ -79,10 +75,7 @@     "build a basic parts library",     "add a 3d model to a component",     "make a fusion 360 library with 3d",-    "set up the fusion bridge",-    "fusion dashboard",-    "fusion status page",-    "open the fusion launch page"+    "set up the fusion bridge"   ],   "discovery_pitch": "Use the Adom Bridge Fusion 360 bridge when the user wants to DRIVE Autodesk Fusion 360 from the cloud: open/search their team's Fusion cloud designs, lay out a PCB or do electronics, export STEP/IGES/STL/Gerbers/BOM, or build a component library with real rendering 3D. It runs Fusion on the user's own machine via Adom Bridge. NOT for web/browser/login/form-filling (that's the native-browser extension) and NOT for KiCad (that's the kicad bridge).",   "brief": "Drive Autodesk Fusion 360 from the cloud via Adom Bridge: component libraries, IPC package generation, board layout, exports (STEP/Gerbers/BOM/CPL), fast APS cloud search, and parametric modeling.",@@ -111,5 +104,9 @@       "label": "Part volume",       "prompt": "How much material does each printed part in this assembly use?"     }-  ]-}+  ],+  "author": {+    "name": "John Lauer",+    "email": "[email protected]"+  }+}\ No newline at end of file
tools/release.shadded+157
@@ -0,0 +1,157 @@+#!/usr/bin/env bash+#+# tools/release.sh <version>   (e.g. tools/release.sh 1.9.175)+# tools/release.sh --bump      (auto: max of all current version files, patch + 1)+#+# THE SINGLE LOCKSTEP RELEASE for the Fusion bridge. The bridge (Python), the dashboard+# (Rust binary), and the wiki PACKAGE all move to ONE version, together, in one command.+#+# Why this exists (John, 2026-08-23): the three artifacts used to be versioned independently+# on a shared 1.9.x line, so they LOOKED lockstep but silently drifted - and a dashboard ship+# that did `release upload` + `repo push` but forgot `pkg publish` left every `pkg install`+# resolving a stale dashboard binary (the DASH_VERSION publish-lag, caught twice by the dock+# thread). Field best-practice for one-product-installed-as-a-unit is lockstep versioning; a+# convention a human must remember is exactly what failed, so this script IS the convention.+#+# It also fixes the class of "silent publish failure": EVERY upload/publish/install step is+# checked for success and the script EXITS NONZERO on the first failure - it never sails past+# a failed publish and reports success.++set -euo pipefail++REPO="adom/fusion-bridge"+MANIFEST="adom-bridge-fusion-manifest.json"+RELEASE_FILES="tools/release-files.txt"+DASH_BIN="dashboard/target/release/fusion-dashboard"+DL_BASE="https://wiki.adom.inc/download/${REPO}"+API_MANIFEST="https://wiki.adom.inc/api/v1/pages/fusion-bridge/files/${MANIFEST}"+BRIDGE_TARGET="${BRIDGE_TARGET:-AdomLapper}"+AITHREAD="fusion-bridge dashboard"++cd "$(dirname "$0")/.."   # repo root++die() { echo "❌ release.sh: $*" >&2; exit 1; }+step() { echo; echo "▶ $*"; }++# --- 0. resolve the target version (lockstep = ONE number for everything) ----------------+cur_versions() {+  cat BRIDGE_VERSION 2>/dev/null; echo   # BRIDGE_VERSION has no trailing newline+  sed -n 's/.*"version": "\([0-9.]*\)".*/\1/p' bridge.json page.json package.json 2>/dev/null+  sed -n 's/^version = "\([0-9.]*\)".*/\1/p' dashboard/Cargo.toml 2>/dev/null+}+[ $# -ge 1 ] || die "usage: tools/release.sh <version> | --bump"+if [ "$1" = "--bump" ]; then+  MAX=$(cur_versions | sort -t. -k1,1n -k2,2n -k3,3n | tail -1)+  [ -n "$MAX" ] || die "could not read any current version"+  IFS=. read -r MA MI PA <<<"$MAX"; VERSION="${MA}.${MI}.$((PA+1))"+  echo "auto-bump: max current $MAX -> $VERSION"+else+  VERSION="$1"+fi+[[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || die "version must be X.Y.Z, got '$VERSION'"+echo "== LOCKSTEP RELEASE $VERSION =="++# --- 1. preflight gates (the outage lesson: never publish code that won't import) ---------+step "Preflight gates (syntax / shadow / import)"+python3 -c "import ast; ast.parse(open('server.py').read())" || die "server.py syntax error"+python3 tools/shadow_check.py server.py | tail -1 | grep -q "^0 shadowing" || die "shadow_check found shadowing bugs"+python3 -c "import server" >/dev/null 2>&1 || die "server.py fails to IMPORT (would crash-loop the bridge)"+python3 -c "import fusion_detect" >/dev/null 2>&1 || die "fusion_detect.py fails to import"+echo "  gates OK"++# --- 2. set ALL version files to $VERSION (this is the lockstep) --------------------------+step "Setting every version file to $VERSION"+printf '%s' "$VERSION" > BRIDGE_VERSION+python3 - "$VERSION" <<'PY'+import json, sys+v = sys.argv[1]+for f in ("bridge.json", "page.json", "package.json"):+    d = json.load(open(f)); d["version"] = v+    json.dump(d, open(f, "w"), indent=2); open(f, "a").write("\n")+PY+sed -i "s/^version = \"[0-9.]*\"/version = \"$VERSION\"/" dashboard/Cargo.toml+sed -i "s/DASH_VERSION=\"[0-9.]*\"/DASH_VERSION=\"$VERSION\"/" install.sh+# prove they all match now+BAD=$(cur_versions; sed -n 's/DASH_VERSION="\([0-9.]*\)"/\1/p' install.sh)+echo "$BAD" | grep -vx "$VERSION" | grep -q . && { echo "$BAD"; die "version files did not all set to $VERSION"; }+echo "  all 6 files at $VERSION"++# --- 3. build the dashboard (Rust) -------------------------------------------------------+step "Building dashboard binary"+( cd dashboard && cargo build --release ) || die "cargo build failed"+"$DASH_BIN" --version 2>&1 | grep -q "v$VERSION" || die "built dashboard does not report v$VERSION"+echo "  dashboard $($DASH_BIN --version 2>&1 | head -1)"++# --- 4. build the bridge zip -------------------------------------------------------------+step "Building bridge zip"+ZIP="/tmp/${REPO//\//-}-v${VERSION}.zip"+python3 - "$ZIP" "$RELEASE_FILES" <<'PY'+import zipfile, os, sys+zp, listf = sys.argv[1], sys.argv[2]+files = [l.strip() for l in open(listf) if l.strip()]+missing = [f for f in files if not os.path.exists(f)]+if missing: raise SystemExit("missing release files: %s" % missing)+with zipfile.ZipFile(zp, "w", zipfile.ZIP_DEFLATED) as z:+    for f in files: z.write(f, f)+print("  zip", os.path.getsize(zp), "bytes,", len(files), "files")+PY++# --- 5. update the bridge manifest (version/url/sha256/size) ------------------------------+step "Updating $MANIFEST"+SHA=$(sha256sum "$ZIP" | cut -d' ' -f1); SIZE=$(stat -c%s "$ZIP")+python3 - "$VERSION" "$SHA" "$SIZE" "$DL_BASE" "$MANIFEST" <<'PY'+import json, sys+v, sha, size, base, mf = sys.argv[1], sys.argv[2], int(sys.argv[3]), sys.argv[4], sys.argv[5]+m = json.load(open(mf))+m["version"] = v; m["sha256"] = sha; m["size"] = size+m["url"] = f"{base}/{v}/adom-bridge-fusion-v{v}.zip"+json.dump(m, open(mf, "w"), indent=2)+PY++# --- 6. upload BOTH release assets, CHECKED (silent-publish-failure fix) ------------------+# The adom-wiki --json envelope is {status:"ok"|..., data, hints[]} - status is the signal.+ok_check() { python3 -c "import sys,json; d=json.load(sys.stdin); sys.exit(0 if d.get('status')=='ok' else 1)"; }+step "Uploading release assets @ $VERSION (bridge zip + dashboard binary)"+adom-wiki release upload "$REPO" "$VERSION" "$ZIP" --platform windows --arch x64 --json 2>/dev/null | ok_check \+  || die "bridge zip release upload FAILED"+adom-wiki release upload "$REPO" "$VERSION" "$DASH_BIN" --platform linux --arch x64 --json 2>/dev/null | ok_check \+  || die "dashboard binary release upload FAILED"+echo "  both assets uploaded"++# --- 7. commit + push the source (repo layer) --------------------------------------------+step "Commit + repo push"+git add -A+git commit -q -m "release $VERSION (lockstep): bridge + dashboard + package all at $VERSION++Co-Authored-By: Claude Opus 4.8 <[email protected]>" || echo "  (nothing to commit)"+adom-wiki repo push "$REPO" --files server.py fusion_detect.py BRIDGE_VERSION bridge.json \+  page.json package.json "$MANIFEST" dashboard/src/main.rs dashboard/Cargo.toml dashboard/Cargo.lock \+  install.sh --json 2>/dev/null | ok_check || die "repo push FAILED"+echo "  pushed"++# --- 8. publish the PACKAGE, CHECKED (the step the old flow forgot) -----------------------+step "Publishing package"+PUB=$(adom-wiki pkg publish --org adom --json 2>/dev/null || true)+echo "$PUB" | ok_check || die "pkg publish FAILED (status != ok) — output: ${PUB:0:400}"+echo "  published $REPO @ package $VERSION"++# --- 9. install onto the box, CHECKED ----------------------------------------------------+step "bridge_install on $BRIDGE_TARGET"+adom-bridge --ai-thread "$AITHREAD" --target "$BRIDGE_TARGET" bridge_install \+  "{\"manifestUrl\":\"$API_MANIFEST\",\"force\":true}" 2>/dev/null \+  | python3 -c "import sys,json; r=json.load(sys.stdin); d=r.get('data') or r; d=(json.loads(d['output']) if isinstance(d,dict) and d.get('output') else d); sys.exit(0 if (d.get('status')=='ok' or r.get('status')=='ok') else 1)" \+  || die "bridge_install FAILED"+echo "  installed"++# --- 10. VERIFY end-to-end (what actually catches a lockstep break) -----------------------+step "Verifying a clean install resolves $VERSION"+TMP=$(mktemp -d)+( cd "$TMP" && adom-wiki pkg install "$REPO" >/dev/null 2>&1 ) || die "clean pkg install failed"+INSTALLED_DASH=$(find "$TMP" "$HOME/project/adom_modules" -name install.sh -path "*fusion-bridge*" 2>/dev/null \+  | head -1 | xargs -r sed -n 's/DASH_VERSION="\([0-9.]*\)".*/\1/p')+[ "$INSTALLED_DASH" = "$VERSION" ] || die "clean install resolves DASH_VERSION=$INSTALLED_DASH, expected $VERSION"+rm -rf "$TMP"+echo "  clean install DASH_VERSION = $VERSION ✓"++echo+echo "✅ LOCKSTEP RELEASE $VERSION complete: bridge + dashboard + package all at $VERSION, verified."