Files
keep/deploy-cli.sh
Fredrik Johansson 1b4cd9b826 Fix deploy-cli.sh: bundle was completely broken on a real host
Caught live, deployed to the actual target -- the previous version
failed on the real host with "Cannot use import statement outside a
module" (Node 18, no ancestor package.json to signal ESM for an
extensionless file). That surfaced two stacked bugs my own testing had
missed by running the bundle from inside this repo's own directory
tree, which coincidentally supplied both things the standalone
artifact was silently depending on:

1. Module-format ambiguity: an extensionless file with no controlling
   package.json defaults to CommonJS on older Node (no auto-detection
   heuristic before recent versions). Fixed by naming the bundle
   output keep.mjs -- unconditionally ESM on any Node version,
   regardless of extension-less-file heuristics or nearby package.json.

2. The bigger one: crypto.ts's createRequire(import.meta.url) trick
   (needed because libsodium-wrappers' published ESM build follows a
   broken relative import) is opaque to esbuild's static bundler --
   it's a runtime-obtained require reference, not the literal `require`
   token esbuild's bundling recognizes. The previous "18kb bundle"
   never actually contained libsodium-wrappers at all; it silently
   relied on real node_modules being nearby on disk, which was only
   ever true by accident when testing from within this repo. On a
   clean host it threw "Cannot find module 'libsodium-wrappers'".

   Tried forcing static inlining via a literal require() call (esbuild
   does special-case that even in ESM-format output, unlike `import`,
   which is permanently pinned to the "import" resolution condition
   and can't be routed to the package's working CJS build no matter
   what --conditions/--main-fields/--alias combination is tried this
   was tried and confirmed exhaustively). That got real resolution and
   a real 1.7MB bundle, but broke at runtime with "No secure random
   number generator found" -- inlining the WASM engine breaks its own
   Node-crypto feature detection.

   Landed on: ship libsodium-wrappers (and its own dependency,
   libsodium) as real, unmodified package files alongside the ~18kb
   bundle, via a new copy-cli-deps.mjs step, rather than fighting
   further to force single-file inlining. ~1.6MB total, still one tar
   stream over SSH, still the entire deploy step.

deploy-cli.sh now tars dist-bundle/ (bundle + node_modules) instead of
catting a single file, extracts to ~/.keep-cli/ on the target, and
symlinks ~/bin/keep to the entry point inside it.

Verified this time in conditions that actually match the failure: full
isolation (mktemp'd HOME and install dir, no ancestor package.json, no
adjacent node_modules from this repo) for both `node keep.mjs` and
direct shebang execution, plus the exact tar/extract cycle
deploy-cli.sh performs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-15 15:59:44 +02:00

39 lines
1.5 KiB
Bash
Executable File

#!/usr/bin/env bash
# deploy-cli.sh — package the keep CLI (bundle + its one real runtime
# dependency, libsodium-wrappers) and ship it straight to a host over SSH.
# No git clone, no npm install on the target.
#
# Not a single file: libsodium-wrappers' published ESM build is broken
# (a relative import that doesn't resolve outside a bundler-aware
# context — see src/shared/crypto.ts), and statically force-inlining it
# via esbuild breaks its own Node-crypto feature-detection at runtime
# ("No secure random number generator found"). Shipping the real,
# unmodified package alongside a small (~18kb) bundle for everything
# else is more reliable than fighting that — the whole package is still
# only ~1.6MB and this script is still the entire deploy step.
#
# Usage: HOST=user@host ./deploy-cli.sh
set -euo pipefail
HOST="${HOST:?usage: HOST=user@host ./deploy-cli.sh}"
BIN_DIR="${BIN_DIR:-~/bin}"
INSTALL_DIR="${INSTALL_DIR:-~/.keep-cli}"
echo "→ bundling keep CLI…"
npm run build:cli-bundle
echo "→ packaging…"
tar -C dist-bundle -czf /tmp/keep-cli.tar.gz .
echo "→ uploading to $HOST:$INSTALL_DIR"
ssh "$HOST" "mkdir -p $INSTALL_DIR && tar -C $INSTALL_DIR -xzf -" < /tmp/keep-cli.tar.gz
rm /tmp/keep-cli.tar.gz
echo "→ linking $BIN_DIR/keep…"
ssh "$HOST" "mkdir -p $BIN_DIR && chmod +x $INSTALL_DIR/keep.mjs && ln -sf $INSTALL_DIR/keep.mjs $BIN_DIR/keep"
echo "→ verifying…"
ssh "$HOST" "$BIN_DIR/keep --help" | head -1
echo "✓ done — re-run this script any time the CLI changes; safe to overwrite."