Skip to content

Commit 02db58e

Browse files
committed
chore: harden install scripts and add smoke ci
1 parent 21f6e86 commit 02db58e

File tree

7 files changed

+308
-5
lines changed

7 files changed

+308
-5
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
name: Install Smoke
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
workflow_dispatch:
8+
9+
jobs:
10+
install-smoke:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- name: Checkout installer
14+
uses: actions/checkout@v4
15+
16+
- name: Build smoke image (root)
17+
run: docker build -t clawdbot-install-smoke:ci -f scripts/docker/install-sh-smoke/Dockerfile scripts/docker/install-sh-smoke
18+
19+
- name: Run install smoke (root)
20+
env:
21+
CLAWDBOT_INSTALL_URL: file://$GITHUB_WORKSPACE/public/install.sh
22+
CLAWDBOT_NO_ONBOARD: "1"
23+
run: docker run --rm -t -e CLAWDBOT_INSTALL_URL -e CLAWDBOT_NO_ONBOARD clawdbot-install-smoke:ci
24+
25+
- name: Build non-root image
26+
run: docker build -t clawdbot-install-nonroot:ci -f scripts/docker/install-sh-nonroot/Dockerfile scripts/docker/install-sh-nonroot
27+
28+
- name: Run install smoke (non-root + CLI)
29+
env:
30+
CLAWDBOT_INSTALL_URL: file://$GITHUB_WORKSPACE/public/install.sh
31+
CLAWDBOT_INSTALL_CLI_URL: file://$GITHUB_WORKSPACE/public/install-cli.sh
32+
CLAWDBOT_NO_ONBOARD: "1"
33+
run: |
34+
docker run --rm -t \
35+
-e CLAWDBOT_INSTALL_URL \
36+
-e CLAWDBOT_INSTALL_CLI_URL \
37+
-e CLAWDBOT_NO_ONBOARD \
38+
clawdbot-install-nonroot:ci

public/install-cli.sh

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ CLAWDBOT_VERSION="${CLAWDBOT_VERSION:-latest}"
99
NODE_VERSION="${CLAWDBOT_NODE_VERSION:-22.12.0}"
1010
JSON=0
1111
RUN_ONBOARD=0
12+
SET_NPM_PREFIX=0
1213

1314
print_usage() {
1415
cat <<EOF
@@ -19,6 +20,7 @@ Usage: install-cli.sh [options]
1920
--node-version <ver> Node version (default: 22.12.0)
2021
--onboard Run "clawdbot onboard" after install
2122
--no-onboard Skip onboarding (default)
23+
--set-npm-prefix Force npm prefix to ~/.npm-global if current prefix is not writable (Linux)
2224
EOF
2325
}
2426

@@ -79,6 +81,10 @@ parse_args() {
7981
print_usage
8082
exit 0
8183
;;
84+
--set-npm-prefix)
85+
SET_NPM_PREFIX=1
86+
shift
87+
;;
8288
*)
8389
fail "Unknown option: $1"
8490
;;
@@ -158,10 +164,45 @@ install_node() {
158164
emit_json "{\"event\":\"step\",\"name\":\"node\",\"status\":\"ok\",\"version\":\"${NODE_VERSION}\"}"
159165
}
160166

167+
fix_npm_prefix_if_needed() {
168+
# only meaningful on Linux, non-root installs
169+
if [[ "$(os_detect)" != "linux" ]]; then
170+
return
171+
fi
172+
173+
local prefix
174+
prefix="$("$(npm_bin)" config get prefix 2>/dev/null || true)"
175+
if [[ -z "$prefix" ]]; then
176+
return
177+
fi
178+
179+
if [[ -w "$prefix" || -w "${prefix}/lib" ]]; then
180+
return
181+
fi
182+
183+
local target="${HOME}/.npm-global"
184+
mkdir -p "$target"
185+
"$(npm_bin)" config set prefix "$target"
186+
187+
local path_line="export PATH=\\\"${target}/bin:\\$PATH\\\""
188+
for rc in "${HOME}/.bashrc" "${HOME}/.zshrc"; do
189+
if [[ -f "$rc" ]] && ! grep -q ".npm-global" "$rc"; then
190+
echo "$path_line" >> "$rc"
191+
fi
192+
done
193+
194+
export PATH="${target}/bin:${PATH}"
195+
emit_json "{\"event\":\"step\",\"name\":\"npm-prefix\",\"status\":\"ok\",\"prefix\":\"${target//\"/\\\"}\"}"
196+
log "Configured npm prefix to ${target}"
197+
}
198+
161199
install_clawdbot() {
162200
emit_json "{\"event\":\"step\",\"name\":\"clawdbot\",\"status\":\"start\",\"version\":\"${CLAWDBOT_VERSION}\"}"
163201
log "Installing Clawdbot (${CLAWDBOT_VERSION})..."
164202
require_bin git
203+
if [[ "$SET_NPM_PREFIX" -eq 1 ]]; then
204+
fix_npm_prefix_if_needed
205+
fi
165206
"$(npm_bin)" install -g --prefix "$PREFIX" "clawdbot@${CLAWDBOT_VERSION}"
166207
rm -f "${PREFIX}/bin/clawdbot"
167208
cat > "${PREFIX}/bin/clawdbot" <<EOF
@@ -184,9 +225,16 @@ resolve_clawdbot_version() {
184225
main() {
185226
parse_args "$@"
186227

228+
if [[ "${CLAWDBOT_NO_ONBOARD:-0}" == "1" ]]; then
229+
RUN_ONBOARD=0
230+
fi
231+
187232
export PATH="$(node_dir)/bin:${PREFIX}/bin:${PATH}"
188233

189234
install_node
235+
if [[ "$SET_NPM_PREFIX" -eq 1 ]]; then
236+
fix_npm_prefix_if_needed
237+
fi
190238
install_clawdbot
191239

192240
local installed_version

public/install.sh

Lines changed: 99 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,26 @@ pick_tagline() {
138138

139139
TAGLINE=$(pick_tagline)
140140

141+
NO_ONBOARD=${CLAWDBOT_NO_ONBOARD:-0}
142+
143+
parse_args() {
144+
while [[ $# -gt 0 ]]; do
145+
case "$1" in
146+
--no-onboard)
147+
NO_ONBOARD=1
148+
shift
149+
;;
150+
--onboard)
151+
NO_ONBOARD=0
152+
shift
153+
;;
154+
*)
155+
shift
156+
;;
157+
esac
158+
done
159+
}
160+
141161
echo -e "${ACCENT}${BOLD}"
142162
echo " 🦞 Clawdbot Installer"
143163
echo -e "${NC}${ACCENT_DIM} ${TAGLINE}${NC}"
@@ -225,6 +245,67 @@ install_node() {
225245
fi
226246
}
227247

248+
# Check Git
249+
check_git() {
250+
if command -v git &> /dev/null; then
251+
echo -e "${SUCCESS}${NC} Git already installed"
252+
return 0
253+
fi
254+
echo -e "${WARN}${NC} Git not found"
255+
return 1
256+
}
257+
258+
install_git() {
259+
echo -e "${WARN}${NC} Installing Git..."
260+
if [[ "$OS" == "macos" ]]; then
261+
brew install git
262+
elif [[ "$OS" == "linux" ]]; then
263+
if command -v apt-get &> /dev/null; then
264+
sudo apt-get update -y
265+
sudo apt-get install -y git
266+
elif command -v dnf &> /dev/null; then
267+
sudo dnf install -y git
268+
elif command -v yum &> /dev/null; then
269+
sudo yum install -y git
270+
else
271+
echo -e "${ERROR}Error: Could not detect package manager for Git${NC}"
272+
exit 1
273+
fi
274+
fi
275+
echo -e "${SUCCESS}${NC} Git installed"
276+
}
277+
278+
# Fix npm permissions for global installs (Linux)
279+
fix_npm_permissions() {
280+
if [[ "$OS" != "linux" ]]; then
281+
return 0
282+
fi
283+
284+
local npm_prefix
285+
npm_prefix="$(npm config get prefix 2>/dev/null || true)"
286+
if [[ -z "$npm_prefix" ]]; then
287+
return 0
288+
fi
289+
290+
if [[ -w "$npm_prefix" || -w "$npm_prefix/lib" ]]; then
291+
return 0
292+
fi
293+
294+
echo -e "${WARN}${NC} Configuring npm for user-local installs..."
295+
mkdir -p "$HOME/.npm-global"
296+
npm config set prefix "$HOME/.npm-global"
297+
298+
local path_line='export PATH="$HOME/.npm-global/bin:$PATH"'
299+
for rc in "$HOME/.bashrc" "$HOME/.zshrc"; do
300+
if [[ -f "$rc" ]] && ! grep -q ".npm-global" "$rc"; then
301+
echo "$path_line" >> "$rc"
302+
fi
303+
done
304+
305+
export PATH="$HOME/.npm-global/bin:$PATH"
306+
echo -e "${SUCCESS}${NC} npm configured for user installs"
307+
}
308+
228309
# Check for existing Clawdbot installation
229310
check_existing_clawdbot() {
230311
if command -v clawdbot &> /dev/null; then
@@ -279,10 +360,18 @@ main() {
279360
install_node
280361
fi
281362

282-
# Step 3: Clawdbot
363+
# Step 3: Git (required for npm installs that may fetch from git or apply patches)
364+
if ! check_git; then
365+
install_git
366+
fi
367+
368+
# Step 4: npm permissions (Linux)
369+
fix_npm_permissions
370+
371+
# Step 5: Clawdbot
283372
install_clawdbot
284373

285-
# Step 4: Run doctor for migrations if upgrading
374+
# Step 6: Run doctor for migrations if upgrading
286375
if [[ "$is_upgrade" == "true" ]]; then
287376
run_doctor
288377
fi
@@ -301,13 +390,18 @@ main() {
301390
if [[ "$is_upgrade" == "true" ]]; then
302391
echo -e "Upgrade complete. Run ${INFO}clawdbot doctor${NC} to check for additional migrations."
303392
else
304-
echo -e "Starting setup..."
305-
echo ""
306-
exec clawdbot onboard
393+
if [[ "$NO_ONBOARD" == "1" ]]; then
394+
echo -e "Skipping onboard (requested). Run ${INFO}clawdbot onboard${NC} later."
395+
else
396+
echo -e "Starting setup..."
397+
echo ""
398+
exec clawdbot onboard
399+
fi
307400
fi
308401

309402
echo ""
310403
echo -e "FAQ: ${INFO}https://docs.clawd.bot/start/faq${NC}"
311404
}
312405

406+
parse_args "$@"
313407
main
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
FROM ubuntu:24.04
2+
3+
RUN apt-get update \
4+
&& apt-get install -y --no-install-recommends \
5+
bash \
6+
ca-certificates \
7+
curl \
8+
sudo \
9+
&& rm -rf /var/lib/apt/lists/*
10+
11+
RUN useradd -m -s /bin/bash app \
12+
&& echo "app ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/app
13+
14+
USER app
15+
WORKDIR /home/app
16+
17+
ENV NPM_CONFIG_FUND=false
18+
ENV NPM_CONFIG_AUDIT=false
19+
20+
COPY run.sh /usr/local/bin/clawdbot-install-nonroot
21+
RUN sudo chmod +x /usr/local/bin/clawdbot-install-nonroot
22+
23+
ENTRYPOINT ["/usr/local/bin/clawdbot-install-nonroot"]
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
INSTALL_URL="${CLAWDBOT_INSTALL_URL:-https://clawd.bot/install.sh}"
5+
CLI_INSTALL_URL="${CLAWDBOT_INSTALL_CLI_URL:-https://clawd.bot/install-cli.sh}"
6+
7+
echo "==> Pre-flight: ensure git absent"
8+
if command -v git >/dev/null; then
9+
echo "git is present unexpectedly" >&2
10+
exit 1
11+
fi
12+
13+
echo "==> Run installer (non-root user)"
14+
curl -fsSL "$INSTALL_URL" | bash --no-onboard
15+
16+
# Ensure PATH picks up user npm prefix
17+
export PATH="$HOME/.npm-global/bin:$PATH"
18+
19+
echo "==> Verify git installed"
20+
command -v git >/dev/null
21+
22+
echo "==> Verify clawdbot installed"
23+
LATEST_VERSION="$(npm view clawdbot version)"
24+
CMD_PATH="$(command -v clawdbot || true)"
25+
if [[ -z "$CMD_PATH" && -x "$HOME/.npm-global/bin/clawdbot" ]]; then
26+
CMD_PATH="$HOME/.npm-global/bin/clawdbot"
27+
fi
28+
if [[ -z "$CMD_PATH" ]]; then
29+
echo "clawdbot not on PATH" >&2
30+
exit 1
31+
fi
32+
INSTALLED_VERSION="$("$CMD_PATH" --version 2>/dev/null | head -n 1 | tr -d '\r')"
33+
34+
echo "installed=$INSTALLED_VERSION expected=$LATEST_VERSION"
35+
if [[ "$INSTALLED_VERSION" != "$LATEST_VERSION" ]]; then
36+
echo "ERROR: expected clawdbot@$LATEST_VERSION, got @$INSTALLED_VERSION" >&2
37+
exit 1
38+
fi
39+
40+
echo "==> Sanity: CLI runs"
41+
"$CMD_PATH" --help >/dev/null
42+
43+
echo "==> Run CLI installer (should also succeed non-root)"
44+
curl -fsSL "$CLI_INSTALL_URL" | bash -s -- --set-npm-prefix --no-onboard
45+
46+
echo "OK"
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
FROM node:22-bookworm-slim
2+
3+
RUN apt-get update \
4+
&& apt-get install -y --no-install-recommends \
5+
bash \
6+
ca-certificates \
7+
curl \
8+
sudo \
9+
&& rm -rf /var/lib/apt/lists/*
10+
11+
COPY run.sh /usr/local/bin/clawdbot-install-smoke
12+
RUN chmod +x /usr/local/bin/clawdbot-install-smoke
13+
14+
ENTRYPOINT ["/usr/local/bin/clawdbot-install-smoke"]
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
INSTALL_URL="${CLAWDBOT_INSTALL_URL:-https://clawd.bot/install.sh}"
5+
6+
echo "==> Resolve npm versions"
7+
LATEST_VERSION="$(npm view clawdbot version)"
8+
PREVIOUS_VERSION="$(node - <<'NODE'
9+
const { execSync } = require("node:child_process");
10+
11+
const versions = JSON.parse(execSync("npm view clawdbot versions --json", { encoding: "utf8" }));
12+
if (!Array.isArray(versions) || versions.length === 0) {
13+
process.exit(1);
14+
}
15+
const previous = versions.length >= 2 ? versions[versions.length - 2] : versions[0];
16+
process.stdout.write(previous);
17+
NODE
18+
)"
19+
20+
echo "latest=$LATEST_VERSION previous=$PREVIOUS_VERSION"
21+
22+
echo "==> Preinstall previous (forces installer upgrade path)"
23+
npm install -g "clawdbot@${PREVIOUS_VERSION}"
24+
25+
echo "==> Run official installer one-liner"
26+
curl -fsSL "$INSTALL_URL" | bash --no-onboard
27+
28+
echo "==> Verify installed version"
29+
INSTALLED_VERSION="$(clawdbot --version 2>/dev/null | head -n 1 | tr -d '\r')"
30+
echo "installed=$INSTALLED_VERSION expected=$LATEST_VERSION"
31+
32+
if [[ "$INSTALLED_VERSION" != "$LATEST_VERSION" ]]; then
33+
echo "ERROR: expected clawdbot@$LATEST_VERSION, got clawdbot@$INSTALLED_VERSION" >&2
34+
exit 1
35+
fi
36+
37+
echo "==> Sanity: CLI runs"
38+
clawdbot --help >/dev/null
39+
40+
echo "OK"

0 commit comments

Comments
 (0)