From 745b9c49675336c8d294634fcb4407d159869c5e Mon Sep 17 00:00:00 2001 From: Jarvis Date: Wed, 24 Jun 2026 16:54:10 -0500 Subject: [PATCH] feat(installer): --dev flag builds CLI + gateway from source MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds `install.sh --dev` (and MOSAIC_DEV=1): instead of installing @mosaicstack/mosaic from the registry @latest, download the monorepo archive at --ref, `pnpm install` + build, `pnpm pack` both @mosaicstack/mosaic and @mosaicstack/gateway, and `npm install -g` both local tarballs. ZERO registry writes — no dev dist-tags. Workspace deps resolve from the registry at the versions `pnpm pack` rewrites `workspace:*` into. Exports MOSAIC_GATEWAY_SKIP_NPM_INSTALL=1 so the auto-launched wizard keeps the source-built global gateway instead of overwriting it with the registry build. Refactors the archive download into a shared ensure_monorepo helper reused by both the framework install (Part 1) and the dev build (Part 2). Lets a branch be tested end-to-end on a clean machine before cutting a release, reducing semver churn. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01RMoEx7hfdFGjUiCHuN1RRi --- tools/install.sh | 149 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 128 insertions(+), 21 deletions(-) diff --git a/tools/install.sh b/tools/install.sh index 49f8874..00e306b 100755 --- a/tools/install.sh +++ b/tools/install.sh @@ -16,6 +16,10 @@ # --framework Install/upgrade framework only (skip npm CLI) # --cli Install/upgrade npm CLI only (skip framework) # --ref Git ref for framework archive (default: main) +# --dev Build CLI + gateway FROM SOURCE at --ref instead of the +# registry @latest. Zero registry writes — packs local +# tarballs and installs them globally. Use to test a branch +# end-to-end before cutting a release. # --yes Accept all defaults; headless/non-interactive install # --no-auto-launch Skip automatic mosaic wizard + gateway install on first install # --uninstall Reverse the install: remove framework dir, CLI package, and npmrc line @@ -27,6 +31,7 @@ # MOSAIC_PREFIX — npm global prefix (default: ~/.npm-global) # MOSAIC_NO_COLOR — disable colour (set to 1) # MOSAIC_REF — git ref for framework (default: main) +# MOSAIC_DEV — equivalent to --dev (set to 1) # MOSAIC_ASSUME_YES — equivalent to --yes (set to 1) # ────────────────────────────────────────────────────────────────────────────── # @@ -43,6 +48,7 @@ FLAG_CLI=true FLAG_NO_AUTO_LAUNCH=false FLAG_YES=false FLAG_UNINSTALL=false +FLAG_DEV=false GIT_REF="${MOSAIC_REF:-main}" # MOSAIC_ASSUME_YES env var acts the same as --yes @@ -50,12 +56,18 @@ if [[ "${MOSAIC_ASSUME_YES:-0}" == "1" ]]; then FLAG_YES=true fi +# MOSAIC_DEV env var acts the same as --dev +if [[ "${MOSAIC_DEV:-0}" == "1" ]]; then + FLAG_DEV=true +fi + while [[ $# -gt 0 ]]; do case "$1" in --check) FLAG_CHECK=true; shift ;; --framework) FLAG_CLI=false; shift ;; --cli) FLAG_FRAMEWORK=false; shift ;; --ref) GIT_REF="${2:-main}"; shift 2 ;; + --dev) FLAG_DEV=true; shift ;; --yes|-y) FLAG_YES=true; shift ;; --no-auto-launch) FLAG_NO_AUTO_LAUNCH=true; shift ;; --uninstall) FLAG_UNINSTALL=true; shift ;; @@ -72,6 +84,17 @@ CLI_PKG="${SCOPE}/mosaic" REPO_BASE="https://git.mosaicstack.dev/mosaicstack/stack" ARCHIVE_URL="${REPO_BASE}/archive/${GIT_REF}.tar.gz" +# In dev (build-from-source) mode the gateway is installed globally from a +# locally-built tarball. Tell the wizard / gateway-config stage NOT to overwrite +# it with the registry @latest build (honored by gatewayConfigStage). +if [[ "$FLAG_DEV" == "true" ]]; then + export MOSAIC_GATEWAY_SKIP_NPM_INSTALL=1 +fi + +# Shared monorepo checkout (populated on demand by ensure_monorepo). +WORK_DIR="" +EXTRACTED_DIR="" + # ─── uninstall path ─────────────────────────────────────────────────────────── # Shell-level uninstall for when the CLI is broken or not available. # Handles: framework directory, npm CLI package, npmrc scope line. @@ -239,6 +262,91 @@ framework_version() { fi } +# Download + extract the monorepo archive at $GIT_REF exactly once per run. +# Sets the script-level EXTRACTED_DIR to the repo root. Reused by both the +# framework install (Part 1) and the dev build-from-source path (Part 2). +ensure_monorepo() { + if [[ -n "$EXTRACTED_DIR" ]] && [[ -d "$EXTRACTED_DIR" ]]; then + return 0 + fi + + require_cmd tar + + WORK_DIR="$(mktemp -d "${TMPDIR:-/tmp}/mosaic-install-XXXXXX")" + # shellcheck disable=SC2317 + cleanup_work() { [[ -n "$WORK_DIR" ]] && rm -rf "$WORK_DIR"; } + trap cleanup_work EXIT + + info "Downloading source from ${GIT_REF}…" + if command -v curl &>/dev/null; then + curl -fsSL "$ARCHIVE_URL" | tar xz -C "$WORK_DIR" + elif command -v wget &>/dev/null; then + wget -qO- "$ARCHIVE_URL" | tar xz -C "$WORK_DIR" + else + fail "curl or wget required to download source." + exit 1 + fi + + # Gitea archives extract to / inside the work dir + EXTRACTED_DIR="$(find "$WORK_DIR" -maxdepth 1 -mindepth 1 -type d | head -1)" + if [[ -z "$EXTRACTED_DIR" ]] || [[ ! -d "$EXTRACTED_DIR" ]]; then + fail "Could not locate extracted source in archive." + ls -la "$WORK_DIR" >&2 + exit 1 + fi +} + +# Build @mosaicstack/mosaic + @mosaicstack/gateway from source and install both +# globally from locally-packed tarballs. ZERO registry writes. Workspace deps +# (brain/config/db/…) are pulled from the registry at the versions pinned in +# each package.json — `pnpm pack` rewrites `workspace:*` to those versions. +install_cli_from_source() { + local src="$EXTRACTED_DIR" + local out_dir="$WORK_DIR/dist-tarballs" + mkdir -p "$out_dir" + + # pnpm via corepack (ships with Node >= 16.9; required by Node >= 20 preflight) + if ! command -v pnpm &>/dev/null; then + info "Activating pnpm via corepack…" + corepack enable >/dev/null 2>&1 || true + corepack prepare pnpm@latest --activate >/dev/null 2>&1 || true + fi + require_cmd pnpm + + info "Installing workspace dependencies (pnpm install)…" + ( cd "$src" && pnpm install ) 2>&1 | sed 's/^/ /' + + info "Building CLI + gateway from source…" + ( cd "$src" && pnpm --filter "@mosaicstack/mosaic..." --filter "@mosaicstack/gateway..." run build ) 2>&1 | sed 's/^/ /' + + info "Packing local tarballs…" + ( cd "$src/packages/mosaic" && pnpm pack --pack-destination "$out_dir" ) 2>&1 | sed 's/^/ /' + ( cd "$src/apps/gateway" && pnpm pack --pack-destination "$out_dir" ) 2>&1 | sed 's/^/ /' + + local cli_tgz gw_tgz + cli_tgz="$(ls -1t "$out_dir"/mosaicstack-mosaic-*.tgz 2>/dev/null | head -1)" + gw_tgz="$(ls -1t "$out_dir"/mosaicstack-gateway-*.tgz 2>/dev/null | head -1)" + + if [[ ! -f "$cli_tgz" ]]; then + fail "CLI tarball was not produced by pnpm pack." + exit 1 + fi + if [[ ! -f "$gw_tgz" ]]; then + fail "Gateway tarball was not produced by pnpm pack." + exit 1 + fi + + # Gateway first so it is present globally before the CLI's wizard runs (which + # skips its own gateway install via MOSAIC_GATEWAY_SKIP_NPM_INSTALL=1). + info "Installing gateway from source tarball (global)…" + npm install -g "$gw_tgz" --prefix="$PREFIX" 2>&1 | sed 's/^/ /' + + info "Installing CLI from source tarball (global)…" + npm install -g "$cli_tgz" --prefix="$PREFIX" 2>&1 | sed 's/^/ /' + + ok "Installed from source: CLI $(installed_cli_version)" +} + # ─── preflight ──────────────────────────────────────────────────────────────── require_cmd node @@ -282,25 +390,8 @@ if [[ "$FLAG_FRAMEWORK" == "true" ]]; then warn "Framework not installed." fi else - # Download repo archive and extract framework - require_cmd tar - - WORK_DIR="$(mktemp -d "${TMPDIR:-/tmp}/mosaic-install-XXXXXX")" - cleanup_work() { rm -rf "$WORK_DIR"; } - trap cleanup_work EXIT - - info "Downloading framework from ${GIT_REF}…" - if command -v curl &>/dev/null; then - curl -fsSL "$ARCHIVE_URL" | tar xz -C "$WORK_DIR" - elif command -v wget &>/dev/null; then - wget -qO- "$ARCHIVE_URL" | tar xz -C "$WORK_DIR" - else - fail "curl or wget required to download framework." - exit 1 - fi - - # Gitea archives extract to / inside the work dir - EXTRACTED_DIR="$(find "$WORK_DIR" -maxdepth 1 -mindepth 1 -type d | head -1)" + # Download repo archive and extract framework (shared with the dev build) + ensure_monorepo FRAMEWORK_SRC="$EXTRACTED_DIR/packages/mosaic/framework" if [[ ! -d "$FRAMEWORK_SRC" ]]; then @@ -356,7 +447,11 @@ if [[ "$FLAG_CLI" == "true" ]]; then fi CURRENT="$(installed_cli_version)" - LATEST="$(latest_cli_version)" + if [[ "$FLAG_DEV" == "true" ]]; then + LATEST="" + else + LATEST="$(latest_cli_version)" + fi if [[ -n "$CURRENT" ]]; then dim " Installed: ${CLI_PKG}@${CURRENT}" @@ -364,7 +459,9 @@ if [[ "$FLAG_CLI" == "true" ]]; then dim " Installed: (none)" fi - if [[ -n "$LATEST" ]]; then + if [[ "$FLAG_DEV" == "true" ]]; then + dim " Source: ${REPO_BASE} (ref: ${GIT_REF}, build-from-source)" + elif [[ -n "$LATEST" ]]; then dim " Latest: ${CLI_PKG}@${LATEST}" else dim " Latest: (registry unreachable)" @@ -383,6 +480,16 @@ if [[ "$FLAG_CLI" == "true" ]]; then else ok "Up to date (or ahead of registry)." fi + elif [[ "$FLAG_DEV" == "true" ]]; then + info "Dev mode — building CLI + gateway from source at ref ${GIT_REF}…" + ensure_monorepo + install_cli_from_source + + # PATH check for npm prefix + if [[ ":$PATH:" != *":$PREFIX/bin:"* ]]; then + warn "$PREFIX/bin is not on your PATH" + dim " Add to your shell rc: export PATH=\"$PREFIX/bin:\$PATH\"" + fi else if [[ -z "$LATEST" ]]; then warn "Could not reach registry at $REGISTRY — skipping npm CLI."