build-bundle.sh 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
  1. #!/usr/bin/env bash
  2. #
  3. # Build a self-contained CodeGraph bundle: an official Node runtime + the
  4. # compiled app + its production deps, so CodeGraph runs with NO system Node and
  5. # NO native build — node:sqlite is built into the bundled Node. One archive per
  6. # platform; the recipe is identical across platforms (only the Node download
  7. # differs), so a CI matrix produces all of them.
  8. #
  9. # Usage:
  10. # scripts/build-bundle.sh <target> [node-version]
  11. # target: darwin-arm64 | darwin-x64 | linux-x64 | linux-arm64
  12. # node-version: e.g. v24.16.0 (default below; pin for reproducible builds)
  13. #
  14. # Output: release/codegraph-<target>.tar.gz (extracts to codegraph-<target>/)
  15. #
  16. # NOTE: does not cross-compile — the bundled Node binary is the official build
  17. # for <target>, but to *run-test* a bundle you must be on that platform.
  18. set -euo pipefail
  19. TARGET="${1:?usage: build-bundle.sh <target> [node-version]}"
  20. NODE_VERSION="${2:-v24.16.0}"
  21. ROOT="$(cd "$(dirname "$0")/.." && pwd)"
  22. OUT="$ROOT/release"
  23. WORK="$(mktemp -d)"
  24. trap 'rm -rf "$WORK"' EXIT
  25. NODE_DIST="node-${NODE_VERSION}-${TARGET}"
  26. NODE_URL="https://nodejs.org/dist/${NODE_VERSION}/${NODE_DIST}.tar.gz"
  27. echo "[bundle] target=${TARGET} node=${NODE_VERSION}"
  28. # 1. Download + extract the official Node runtime for the target platform.
  29. echo "[bundle] downloading ${NODE_URL}"
  30. curl -fsSL "$NODE_URL" -o "$WORK/node.tar.gz"
  31. tar -xzf "$WORK/node.tar.gz" -C "$WORK"
  32. NODE_BIN="$WORK/${NODE_DIST}/bin/node"
  33. [ -f "$NODE_BIN" ] || { echo "[bundle] error: node binary not found in tarball" >&2; exit 1; }
  34. # 2. Build the app (compiled JS + copied wasm/schema assets).
  35. echo "[bundle] building app"
  36. ( cd "$ROOT" && npm run build >/dev/null )
  37. # 3. Stage: vendored node + app + production-only deps + launcher.
  38. STAGE="$WORK/codegraph-${TARGET}"
  39. mkdir -p "$STAGE/lib" "$STAGE/bin"
  40. cp "$NODE_BIN" "$STAGE/node"
  41. cp -R "$ROOT/dist" "$STAGE/lib/dist"
  42. cp "$ROOT/package.json" "$ROOT/package-lock.json" "$STAGE/lib/"
  43. echo "[bundle] installing production dependencies"
  44. ( cd "$STAGE/lib" && npm ci --omit=dev --ignore-scripts >/dev/null 2>&1 )
  45. rm -f "$STAGE/lib/package-lock.json"
  46. # 4. Launcher: exec the vendored Node with the app entry. `exec` replaces the
  47. # shell so there's a single process, and the absolute path means the bundled
  48. # Node is used regardless of what's (or isn't) on the user's PATH.
  49. cat > "$STAGE/bin/codegraph" <<'LAUNCH'
  50. #!/bin/sh
  51. DIR="$(cd "$(dirname "$0")/.." && pwd)"
  52. exec "$DIR/node" "$DIR/lib/dist/bin/codegraph.js" "$@"
  53. LAUNCH
  54. chmod +x "$STAGE/bin/codegraph"
  55. # 5. Archive.
  56. mkdir -p "$OUT"
  57. ARCHIVE="$OUT/codegraph-${TARGET}.tar.gz"
  58. # --no-xattrs: don't embed macOS extended attributes (com.apple.provenance),
  59. # which make GNU tar warn noisily when the archive is extracted on Linux.
  60. tar --no-xattrs -czf "$ARCHIVE" -C "$WORK" "codegraph-${TARGET}"
  61. echo "[bundle] wrote ${ARCHIVE} ($(du -h "$ARCHIVE" | cut -f1))"