Skip to content

Commit 18e81f0

Browse files
committed
Improve bun detection in dashboard and CLI install scripts
Add explicit candidate paths (/root/.bun/bin/bun, /home/vscode/.bun/bin/bun), detailed error output listing all checked locations, and PATH export so downstream bare bun calls resolve correctly.
1 parent f3d2169 commit 18e81f0

File tree

2 files changed

+260
-15
lines changed

2 files changed

+260
-15
lines changed

container/.devcontainer/features/codeforge-cli/install.sh

Lines changed: 96 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,34 +11,115 @@ if [ "${VERSION}" = "none" ]; then
1111
exit 0
1212
fi
1313

14-
echo "[codeforge-cli] Installing self-bootstrapping wrapper..."
14+
echo "[codeforge-cli] Installing @coredirective/cf-cli..."
1515

16-
# Write the wrapper script that runs the CLI from workspace source.
17-
# The workspace is not mounted during feature install (Docker build),
18-
# so the wrapper defers bun install to first invocation.
16+
# === FIND BUN ===
17+
BUN=""
18+
19+
for candidate in \
20+
"$(command -v bun 2>/dev/null || true)" \
21+
"/usr/local/bin/bun" \
22+
"/root/.bun/bin/bun" \
23+
"/home/vscode/.bun/bin/bun"; do
24+
if [ -n "$candidate" ] && [ -x "$candidate" ]; then
25+
BUN="$candidate"
26+
break
27+
fi
28+
done
29+
30+
if [ -z "$BUN" ]; then
31+
echo "[codeforge-cli] ERROR: bun is not available"
32+
echo " Checked:"
33+
echo " - PATH"
34+
echo " - /usr/local/bin/bun"
35+
echo " - /root/.bun/bin/bun"
36+
echo " - /home/vscode/.bun/bin/bun"
37+
exit 1
38+
fi
39+
40+
# Ensure downstream commands work
41+
export PATH="$(dirname "$BUN"):$PATH"
42+
43+
echo "[codeforge-cli] Using bun at: $BUN"
44+
45+
# === INSTALL FROM NPM ===
46+
# Install globally — this runs as root during Docker build
47+
if [ "${VERSION}" = "latest" ]; then
48+
"$BUN" install -g @coredirective/cf-cli
49+
else
50+
"$BUN" install -g "@coredirective/cf-cli@${VERSION}"
51+
fi
52+
53+
# === SYMLINK TO SYSTEM PATH ===
54+
# bun install -g puts binaries in the current user's global bin (e.g. /root/.bun/bin/).
55+
# During Docker build this is root's dir, but at runtime the user is vscode.
56+
# Symlink to /usr/local/bin/ so the binary is on everyone's PATH.
57+
BUN_GLOBAL_BIN="$("$BUN" pm bin -g 2>/dev/null || echo "$HOME/.bun/bin")"
58+
INSTALLED_BIN="${BUN_GLOBAL_BIN}/codeforge"
59+
60+
if [ -x "$INSTALLED_BIN" ]; then
61+
echo "[codeforge-cli] Installed binary found at: $INSTALLED_BIN"
62+
else
63+
echo "[codeforge-cli] WARNING: Expected binary not found at $INSTALLED_BIN"
64+
echo " Searching for it..."
65+
INSTALLED_BIN="$(find /root/.bun "$HOME/.bun" /usr/local -name codeforge -type f 2>/dev/null | head -1 || true)"
66+
if [ -z "$INSTALLED_BIN" ]; then
67+
echo "[codeforge-cli] ERROR: Could not find installed codeforge binary"
68+
exit 1
69+
fi
70+
echo "[codeforge-cli] Found at: $INSTALLED_BIN"
71+
fi
72+
73+
# === WRITE DEV-FALLBACK WRAPPER ===
74+
# When the CodeForge monorepo is mounted (developers), run from source.
75+
# Otherwise, use the globally installed NPM package via symlink.
1976
cat > /usr/local/bin/codeforge <<'WRAPPER'
2077
#!/bin/bash
2178
set -euo pipefail
2279
2380
CLI_DIR="${WORKSPACE_ROOT:-/workspaces}/cli"
2481
BUN="${BUN:-$(command -v bun 2>/dev/null || echo "$HOME/.bun/bin/bun")}"
2582
26-
if [ ! -d "$CLI_DIR" ]; then
27-
echo "codeforge: CLI source not found at $CLI_DIR" >&2
28-
echo "Ensure the workspace is mounted and contains the cli/ directory." >&2
29-
exit 1
83+
# Dev mode: if workspace cli/ source exists, use it
84+
if [ -d "$CLI_DIR/src" ]; then
85+
if [ ! -d "$CLI_DIR/node_modules" ]; then
86+
echo "codeforge: bootstrapping dev dependencies..." >&2
87+
"$BUN" install --cwd "$CLI_DIR" --frozen-lockfile >/dev/null 2>&1 || \
88+
"$BUN" install --cwd "$CLI_DIR" >/dev/null 2>&1
89+
fi
90+
exec "$BUN" "$CLI_DIR/src/index.ts" "$@"
3091
fi
3192
32-
if [ ! -d "$CLI_DIR/node_modules" ]; then
33-
echo "codeforge: bootstrapping dependencies..." >&2
34-
"$BUN" install --cwd "$CLI_DIR" --frozen-lockfile >/dev/null 2>&1 || \
35-
"$BUN" install --cwd "$CLI_DIR" >/dev/null 2>&1
93+
# Production mode: use globally installed package
94+
# Resolve bun global bin at runtime (not build time) for correct user context
95+
GLOBAL_BIN="$("$BUN" pm bin -g 2>/dev/null || echo "$HOME/.bun/bin")"
96+
if [ -x "$GLOBAL_BIN/codeforge" ] && [ "$GLOBAL_BIN/codeforge" != "$0" ]; then
97+
exec "$GLOBAL_BIN/codeforge" "$@"
3698
fi
3799
38-
exec "$BUN" "$CLI_DIR/src/index.ts" "$@"
100+
# Fallback: search common locations
101+
for candidate in /root/.bun/bin/codeforge /home/*/.bun/bin/codeforge; do
102+
if [ -x "$candidate" ] && [ "$candidate" != "$0" ]; then
103+
exec "$candidate" "$@"
104+
fi
105+
done
106+
107+
echo "codeforge: CLI package not found. Install with: bun install -g @coredirective/cf-cli" >&2
108+
exit 1
39109
WRAPPER
40110

41111
chmod +x /usr/local/bin/codeforge
42112

43-
echo "[codeforge-cli] Wrapper installed at /usr/local/bin/codeforge"
44-
echo "[codeforge-cli] CLI will bootstrap from workspace source on first use"
113+
# === VERIFY ===
114+
echo "[codeforge-cli] Verifying installation..."
115+
if "$INSTALLED_BIN" --help &>/dev/null; then
116+
echo "[codeforge-cli] ✓ codeforge command is working"
117+
else
118+
echo "[codeforge-cli] WARNING: Could not verify (may work once workspace is mounted)"
119+
fi
120+
121+
echo ""
122+
echo "[codeforge-cli] Installation complete"
123+
echo " Package: @coredirective/cf-cli@${VERSION}"
124+
echo " Command: codeforge"
125+
echo " Dev mode: auto-enabled when /workspaces/cli/ exists"
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
#!/bin/bash
2+
# SPDX-License-Identifier: GPL-3.0-only
3+
# Copyright (c) 2026 Marcus Krueger
4+
set -euo pipefail
5+
6+
# === IMPORT OPTIONS ===
7+
DASH_VERSION="${VERSION:-latest}"
8+
PORT="${PORT:-7847}"
9+
AUTOSTART="${AUTOSTART:-true}"
10+
USERNAME="${USERNAME:-automatic}"
11+
12+
# Skip installation if version is "none"
13+
if [ "${DASH_VERSION}" = "none" ]; then
14+
echo "[codeforge-dashboard] Skipping installation (version=none)"
15+
exit 0
16+
fi
17+
18+
echo "[codeforge-dashboard] Installing @coredirective/cf-dash..."
19+
20+
# === FIND BUN ===
21+
BUN=""
22+
23+
for candidate in \
24+
"$(command -v bun 2>/dev/null || true)" \
25+
"/usr/local/bin/bun" \
26+
"/root/.bun/bin/bun" \
27+
"/home/vscode/.bun/bin/bun"; do
28+
if [ -n "$candidate" ] && [ -x "$candidate" ]; then
29+
BUN="$candidate"
30+
break
31+
fi
32+
done
33+
34+
if [ -z "$BUN" ]; then
35+
echo "[codeforge-dashboard] ERROR: bun is not available"
36+
echo " Checked:"
37+
echo " - PATH"
38+
echo " - /usr/local/bin/bun"
39+
echo " - /root/.bun/bin/bun"
40+
echo " - /home/vscode/.bun/bin/bun"
41+
exit 1
42+
fi
43+
44+
# Ensure downstream commands work
45+
export PATH="$(dirname "$BUN"):$PATH"
46+
47+
echo "[codeforge-dashboard] Using bun at: $BUN"
48+
49+
# === DETECT USER ===
50+
if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then
51+
USERNAME=""
52+
for CURRENT_USER in vscode node codespace; do
53+
if id -u "${CURRENT_USER}" >/dev/null 2>&1; then
54+
USERNAME=${CURRENT_USER}
55+
break
56+
fi
57+
done
58+
[ -z "${USERNAME}" ] && USERNAME=root
59+
elif [ "${USERNAME}" = "none" ] || ! id -u "${USERNAME}" >/dev/null 2>&1; then
60+
USERNAME=root
61+
fi
62+
63+
echo "[codeforge-dashboard] Installing for user: ${USERNAME}"
64+
65+
# === INSTALL FROM NPM ===
66+
# Install globally — this runs as root during Docker build
67+
if [ "${DASH_VERSION}" = "latest" ]; then
68+
"$BUN" install -g @coredirective/cf-dash
69+
else
70+
"$BUN" install -g "@coredirective/cf-dash@${DASH_VERSION}"
71+
fi
72+
73+
# === SYMLINK TO SYSTEM PATH ===
74+
# bun install -g puts binaries in the current user's global bin (e.g. /root/.bun/bin/).
75+
# During Docker build this is root's dir, but at runtime the user is vscode.
76+
# Create a wrapper at /usr/local/bin/ so the binary is on everyone's PATH.
77+
BUN_GLOBAL_BIN="$("$BUN" pm bin -g 2>/dev/null || echo "$HOME/.bun/bin")"
78+
INSTALLED_BIN="${BUN_GLOBAL_BIN}/codeforge-dashboard"
79+
80+
if [ ! -x "$INSTALLED_BIN" ]; then
81+
echo "[codeforge-dashboard] WARNING: Expected binary not found at $INSTALLED_BIN"
82+
echo " Searching for it..."
83+
INSTALLED_BIN="$(find /root/.bun "$HOME/.bun" /usr/local -name codeforge-dashboard -type f 2>/dev/null | head -1 || true)"
84+
if [ -z "$INSTALLED_BIN" ]; then
85+
echo "[codeforge-dashboard] ERROR: Could not find installed codeforge-dashboard binary"
86+
exit 1
87+
fi
88+
fi
89+
90+
echo "[codeforge-dashboard] Installed binary found at: $INSTALLED_BIN"
91+
92+
# Create a system-wide wrapper that resolves the real binary at runtime
93+
cat > /usr/local/bin/codeforge-dashboard <<'WRAPPER'
94+
#!/bin/bash
95+
set -euo pipefail
96+
97+
BUN="${BUN:-$(command -v bun 2>/dev/null || echo "$HOME/.bun/bin/bun")}"
98+
99+
# Check runtime user's global bin first
100+
GLOBAL_BIN="$("$BUN" pm bin -g 2>/dev/null || echo "$HOME/.bun/bin")"
101+
if [ -x "$GLOBAL_BIN/codeforge-dashboard" ] && [ "$GLOBAL_BIN/codeforge-dashboard" != "$0" ]; then
102+
exec "$GLOBAL_BIN/codeforge-dashboard" "$@"
103+
fi
104+
105+
# Fallback: check root's global bin (installed during Docker build)
106+
for candidate in /root/.bun/bin/codeforge-dashboard /home/*/.bun/bin/codeforge-dashboard; do
107+
if [ -x "$candidate" ] && [ "$candidate" != "$0" ]; then
108+
exec "$candidate" "$@"
109+
fi
110+
done
111+
112+
echo "codeforge-dashboard: not found. Install with: bun install -g @coredirective/cf-dash" >&2
113+
exit 1
114+
WRAPPER
115+
116+
chmod +x /usr/local/bin/codeforge-dashboard
117+
118+
# === VERIFY ===
119+
echo "[codeforge-dashboard] Verifying installation..."
120+
if "$INSTALLED_BIN" --version &>/dev/null; then
121+
echo "[codeforge-dashboard] ✓ codeforge-dashboard command is working"
122+
else
123+
echo "[codeforge-dashboard] WARNING: Could not verify (may need bun in PATH)"
124+
fi
125+
126+
# === AUTOSTART HOOK ===
127+
if [ "${AUTOSTART}" = "true" ]; then
128+
HOOK_DIR="/usr/local/devcontainer-poststart.d"
129+
mkdir -p "$HOOK_DIR"
130+
131+
cat > "${HOOK_DIR}/50-codeforge-dashboard.sh" <<HOOK
132+
#!/bin/bash
133+
# Auto-start CodeForge Dashboard
134+
PORT="\${CODEFORGE_DASHBOARD_PORT:-${PORT}}"
135+
136+
# Check if already running on the target port
137+
if command -v lsof &>/dev/null && lsof -i ":\$PORT" &>/dev/null; then
138+
echo "Dashboard already running on port \$PORT"
139+
exit 0
140+
fi
141+
142+
if ! command -v codeforge-dashboard &>/dev/null; then
143+
echo "codeforge-dashboard not found in PATH, skipping auto-start"
144+
exit 0
145+
fi
146+
147+
nohup codeforge-dashboard --port "\$PORT" --host 0.0.0.0 > /tmp/codeforge-dashboard.log 2>&1 &
148+
echo \$! > /tmp/codeforge-dashboard.pid
149+
echo "Dashboard started on port \$PORT (PID: \$!)"
150+
HOOK
151+
152+
chmod +x "${HOOK_DIR}/50-codeforge-dashboard.sh"
153+
echo "[codeforge-dashboard] Poststart hook installed (auto-launch on port ${PORT})"
154+
else
155+
echo "[codeforge-dashboard] Autostart disabled — run manually: codeforge-dashboard --port ${PORT}"
156+
fi
157+
158+
# === SUMMARY ===
159+
echo ""
160+
echo "[codeforge-dashboard] Installation complete"
161+
echo " Package: @coredirective/cf-dash@${DASH_VERSION}"
162+
echo " Command: codeforge-dashboard"
163+
echo " Port: ${PORT}"
164+
echo " Autostart: ${AUTOSTART}"

0 commit comments

Comments
 (0)