Skip to content
Open
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
60 changes: 52 additions & 8 deletions bin/update-release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,53 @@ source "$SCRIPT_DIR/lib/release-runtime-common.sh"
# shellcheck source=bin/lib/json-common.sh
source "$SCRIPT_DIR/lib/json-common.sh"

# ---------------------------------------------------------------------------
# Resolve the full path to npm. This script runs as root (sudo) where the
# embedded Node runtime is *not* on PATH. Resolution order:
# 1. Agent user's embedded runtime (/home/baudbot_agent/opt/node/bin/npm)
# 2. SUDO_USER's home (admin may have mise/nvm/etc.)
# 3. Standard PATH lookup (last resort)
# ---------------------------------------------------------------------------
resolve_npm_bin() {
local candidate=""

# 1) Agent's embedded runtime
local agent_home="/home/${BAUDBOT_AGENT_USER:-baudbot_agent}"
candidate="$(bb_resolve_runtime_node_bin_dir "$agent_home" 2>/dev/null || true)"
if [ -n "$candidate" ] && [ -x "$candidate/npm" ]; then
echo "$candidate/npm"
return 0
fi

# 2) SUDO_USER's home (admin's local node install — mise, nvm, etc.)
if [ -n "${SUDO_USER:-}" ] && [ "$SUDO_USER" != "root" ]; then
local sudo_home=""
sudo_home="$(bb_resolve_user_home "$SUDO_USER" 2>/dev/null || true)"
if [ -n "$sudo_home" ]; then
local dir=""
for dir in \
"$sudo_home/.local/share/mise/shims" \
"$sudo_home/.nvm/versions/node"/*/bin \
"$sudo_home/.local/bin"; do
# Skip unexpanded globs.
case "$dir" in *\**) continue ;; esac
if [ -x "$dir/npm" ]; then
echo "$dir/npm"
return 0
fi
done
fi
fi

# 3) PATH (may work in non-sudo environments or CI)
if command -v npm >/dev/null 2>&1; then
command -v npm
return 0
fi

return 1
}

cleanup() {
if [ -n "$CHECKOUT_DIR" ] && [ -d "$CHECKOUT_DIR" ]; then
rm -rf "$CHECKOUT_DIR"
Expand Down Expand Up @@ -221,14 +268,11 @@ install_release_bridge_dependencies() {
log "installing production Slack bridge dependencies in release"
rm -rf "$bridge_dir/node_modules"

# Resolve npm from the agent's embedded node runtime, falling back to PATH.
local npm_bin="npm"
local agent_home="/home/${BAUDBOT_AGENT_USER:-baudbot_agent}"
local node_bin_dir=""
node_bin_dir="$(bb_resolve_runtime_node_bin_dir "$agent_home" 2>/dev/null || true)"
if [ -n "$node_bin_dir" ] && [ -x "$node_bin_dir/npm" ]; then
npm_bin="$node_bin_dir/npm"
fi
# Resolve npm via the embedded Node runtime. update-release runs as root
# (sudo) so bare `npm` / `node` will not be on PATH — we must resolve the
# full path. Try: agent home → SUDO_USER home → PATH (last resort).
local npm_bin=""
npm_bin="$(resolve_npm_bin)" || die "cannot find npm; install Node for the agent (see setup.sh) or ensure npm is on PATH"

if [ -f "$bridge_dir/package-lock.json" ]; then
(cd "$bridge_dir" && "$npm_bin" ci --omit=dev)
Expand Down
91 changes: 91 additions & 0 deletions bin/update-release.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -214,13 +214,104 @@ test_release_root_overrides_stale_source_path_env() {
)
}

test_resolve_npm_from_fake_agent_home() {
(
set -euo pipefail
local tmp fake_home npm_path

tmp="$(mktemp -d /tmp/baudbot-update-test.XXXXXX)"
trap 'rm -rf "$tmp"' EXIT

# Create a fake embedded node runtime layout.
fake_home="$tmp/home/baudbot_agent"
mkdir -p "$fake_home/opt/node/bin"
printf '#!/bin/sh\necho fake-npm\n' > "$fake_home/opt/node/bin/npm"
chmod +x "$fake_home/opt/node/bin/npm"
# Create a matching node binary so bb_resolve_runtime_node_bin succeeds.
printf '#!/bin/sh\ntrue\n' > "$fake_home/opt/node/bin/node"
chmod +x "$fake_home/opt/node/bin/node"

# Source the helpers and the resolve_npm_bin function from the script.
# We extract the function by sourcing with BAUDBOT_AGENT_USER set so the
# resolution targets our fake home.
npm_path="$(
source "$REPO_ROOT/bin/lib/shell-common.sh"
source "$REPO_ROOT/bin/lib/paths-common.sh"
source "$REPO_ROOT/bin/lib/runtime-node.sh"
source "$REPO_ROOT/bin/lib/release-common.sh"
source "$REPO_ROOT/bin/lib/release-runtime-common.sh"
source "$REPO_ROOT/bin/lib/json-common.sh"

# Define the function inline (extracted from update-release.sh).
resolve_npm_bin() {
local candidate=""
local agent_home="/home/${BAUDBOT_AGENT_USER:-baudbot_agent}"
candidate="$(bb_resolve_runtime_node_bin_dir "$agent_home" 2>/dev/null || true)"
if [ -n "$candidate" ] && [ -x "$candidate/npm" ]; then
echo "$candidate/npm"
return 0
fi
if command -v npm >/dev/null 2>&1; then
command -v npm
return 0
fi
return 1
}
Comment on lines +246 to +259
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test function is missing step 2 (SUDO_USER home resolution) from actual resolve_npm_bin() implementation. Consider either:

  • Adding test coverage for SUDO_USER paths (mise/nvm/.local/bin)
  • Clarifying comment to say "simplified version" instead of "extracted from"
Prompt To Fix With AI
This is a comment left during a code review.
Path: bin/update-release.test.sh
Line: 246-259

Comment:
Test function is missing step 2 (SUDO_USER home resolution) from actual `resolve_npm_bin()` implementation. Consider either:
- Adding test coverage for SUDO_USER paths (`mise`/`nvm`/`.local/bin`)
- Clarifying comment to say "simplified version" instead of "extracted from"

How can I resolve this? If you propose a fix, please make it concise.


BAUDBOT_AGENT_USER="baudbot_agent"
BAUDBOT_HOME="$fake_home"
# Point the resolution at our fake tree.
BAUDBOT_RUNTIME_NODE_BIN_DIR="$fake_home/opt/node/bin"
resolve_npm_bin
)"

[ "$npm_path" = "$fake_home/opt/node/bin/npm" ]
)
}

test_resolve_npm_fails_when_missing() {
(
set -euo pipefail
local tmp

tmp="$(mktemp -d /tmp/baudbot-update-test.XXXXXX)"
trap 'rm -rf "$tmp"' EXIT

# Empty fake home — no node installed anywhere.
local result=0
(
source "$REPO_ROOT/bin/lib/shell-common.sh"
source "$REPO_ROOT/bin/lib/paths-common.sh"
source "$REPO_ROOT/bin/lib/runtime-node.sh"

resolve_npm_bin() {
local candidate=""
local agent_home="$tmp/home/nobody"
candidate="$(bb_resolve_runtime_node_bin_dir "$agent_home" 2>/dev/null || true)"
if [ -n "$candidate" ] && [ -x "$candidate/npm" ]; then
echo "$candidate/npm"
return 0
fi
# Don't check PATH here — we want to verify the function fails.
return 1
}

resolve_npm_bin
) && result=1

[ "$result" -eq 0 ]
)
}

echo "=== update-release tests ==="
echo ""

run_test "publishes git-free release snapshot" test_publish_git_free_release
run_test "preflight failure keeps current release" test_preflight_failure_keeps_current
run_test "deploy failure keeps current release" test_deploy_failure_keeps_current
run_test "release root overrides stale source env" test_release_root_overrides_stale_source_path_env
run_test "resolves npm from agent embedded runtime" test_resolve_npm_from_fake_agent_home
run_test "resolve_npm_bin fails when npm missing" test_resolve_npm_fails_when_missing

echo ""
echo "=== $PASSED/$TOTAL passed, $FAILED failed ==="
Expand Down
Loading