Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## Unreleased

- `install.sh` installs into `~/.vcx` with a fresh manifest instead of
`bun install --global`. Bun 1.4.0 fails with `refusing to install dependency
with unsafe name` on some machines when the global manifest carries entries
from earlier installs, and the new path does not depend on that state.

## 0.3.0 - 2026-09-02

- Link Vercel's global config directory to the active profile, so a plain
Expand Down
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,12 @@ One line. It installs Bun if you do not have it, then Vercel CLI and vcx.
curl -fsSL https://raw.githubusercontent.com/akrupa-appto/vcx/main/install.sh | sh
```

Set `VCX_REF` to pick a tag, branch, or commit instead of the latest release.
You still need Node.js 20 or newer, because Vercel CLI runs on Node.
The script keeps vcx and its dependencies in `~/.vcx` with their own lockfile
and links `vcx` into Bun's bin directory. It does not touch Bun's global
package list, so a broken global manifest cannot block it. Run it again to
upgrade. Set `VCX_REF` to pick a tag, branch, or commit instead of the latest
release, and `VCX_INSTALL_DIR` to move the directory. You still need Node.js
20 or newer, because Vercel CLI runs on Node.

Already have Bun? Install directly:

Expand Down
54 changes: 40 additions & 14 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,19 @@
# Installs vcx with Bun. Usage:
# curl -fsSL https://raw.githubusercontent.com/akrupa-appto/vcx/main/install.sh | sh
#
# vcx lives in its own directory (default ~/.vcx) with its own lockfile, so a
# broken Bun global manifest cannot block the install. Bin links go into
# Bun's bin directory, which is already on PATH for Bun users.
#
# Environment:
# VCX_REF Git ref to install (tag, branch, or commit). Default: latest release.
# BUN_INSTALL Where Bun lives or gets installed. Default: ~/.bun
# VCX_REF Git ref to install (tag, branch, or commit). Default: latest release.
# VCX_INSTALL_DIR Where vcx and its dependencies live. Default: ~/.vcx
# BUN_INSTALL Where Bun lives or gets installed. Default: ~/.bun
set -eu

REPO="akrupa-appto/vcx"
BUN_INSTALL="${BUN_INSTALL:-$HOME/.bun}"
VCX_INSTALL_DIR="${VCX_INSTALL_DIR:-$HOME/.vcx}"

say() { printf '%s\n' "$*"; }
fail() { say "vcx install: $*" >&2; exit 1; }
Expand All @@ -32,26 +38,46 @@ else
[ -x "$BUN" ] || fail "Bun install did not produce $BUN."
fi
say "Using Bun $("$BUN" --version) at $BUN"
# Bun's global bin directory, the one Bun users already have on PATH.
BIN_DIR="$(HOME="$HOME" "$BUN" pm bin --global 2>/dev/null || true)"
[ -n "$BIN_DIR" ] || BIN_DIR="$BUN_INSTALL/bin"

# 2. Vercel CLI, which vcx wraps
if ! command -v vercel >/dev/null 2>&1; then
say "Installing Vercel CLI ..."
"$BUN" install --global vercel >/dev/null
fi

# 3. vcx itself. The github: form avoids Bun 1.4.0's false "unsafe name" error on tarballs.
# 2. Resolve the vcx ref. The github: form avoids Bun 1.4.0's false
# "unsafe name" error on remote tarballs.
REF="${VCX_REF:-}"
if [ -z "$REF" ]; then
REF="$(curl -fsSL "https://api.github.com/repos/$REPO/releases/latest" 2>/dev/null \
| sed -n 's/.*"tag_name": *"\([^"]*\)".*/\1/p' | head -n 1)"
[ -n "$REF" ] || REF="main"
fi
say "Installing vcx ($REF) ..."
"$BUN" install --global "github:$REPO#$REF" >/dev/null

# 4. Verify
BIN_DIR="$("$BUN" pm bin --global 2>/dev/null || printf '%s' "$BUN_INSTALL/bin")"
[ -x "$BIN_DIR/vcx" ] || fail "expected $BIN_DIR/vcx after install."
# 3. Install vcx and Vercel CLI into a private directory with a fresh manifest.
say "Installing vcx ($REF) into $VCX_INSTALL_DIR ..."
mkdir -p "$VCX_INSTALL_DIR"
cat > "$VCX_INSTALL_DIR/package.json" <<JSON
{
"name": "vcx-install",
"private": true,
"dependencies": {
"vcx-cli": "github:$REPO#$REF",
"vercel": "latest"
}
}
JSON
rm -f "$VCX_INSTALL_DIR/bun.lock" "$VCX_INSTALL_DIR/bun.lockb"
(cd "$VCX_INSTALL_DIR" && "$BUN" install --no-summary >/dev/null)

VCX_BIN="$VCX_INSTALL_DIR/node_modules/vcx-cli/dist/cli.js"
[ -f "$VCX_BIN" ] || fail "expected $VCX_BIN after install."
chmod 755 "$VCX_BIN"

# 4. Bin links. vcx always. vercel only when nothing on PATH provides it.
mkdir -p "$BIN_DIR"
ln -sf "$VCX_BIN" "$BIN_DIR/vcx"
if ! command -v vercel >/dev/null 2>&1; then
ln -sf "$VCX_INSTALL_DIR/node_modules/vercel/dist/vc.js" "$BIN_DIR/vercel"
fi

say "Installed vcx $("$BIN_DIR/vcx" --version)"

if ! command -v vcx >/dev/null 2>&1; then
Expand Down