Skip to content

Commit b4fe8c0

Browse files
committed
ci: fix git installer smoke for pnpm allowlist
1 parent b141735 commit b4fe8c0

File tree

5 files changed

+301
-7
lines changed

5 files changed

+301
-7
lines changed

.github/workflows/install-smoke.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ jobs:
5555
- name: Unit tests (install.sh)
5656
run: bash scripts/test-install-sh-unit.sh
5757

58+
- name: Unit tests (install-cli.sh)
59+
run: bash scripts/test-install-cli-unit.sh
60+
5861
install-smoke:
5962
runs-on: ubuntu-latest
6063
steps:

public/install-cli.sh

Lines changed: 105 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ GIT_UPDATE="${OPENCLAW_GIT_UPDATE:-1}"
1515
JSON=0
1616
RUN_ONBOARD=0
1717
SET_NPM_PREFIX=0
18+
PNPM_CMD=()
1819

1920
print_usage() {
2021
cat <<EOF
@@ -280,6 +281,64 @@ npm_bin() {
280281
echo "$(node_dir)/bin/npm"
281282
}
282283

284+
set_pnpm_cmd() {
285+
PNPM_CMD=("$@")
286+
}
287+
288+
pnpm_cmd_is_ready() {
289+
if [[ ${#PNPM_CMD[@]} -eq 0 ]]; then
290+
return 1
291+
fi
292+
"${PNPM_CMD[@]}" --version >/dev/null 2>&1
293+
}
294+
295+
detect_pnpm_cmd() {
296+
if [[ -x "${PREFIX}/bin/pnpm" ]]; then
297+
set_pnpm_cmd "${PREFIX}/bin/pnpm"
298+
return 0
299+
fi
300+
if command -v pnpm >/dev/null 2>&1; then
301+
set_pnpm_cmd pnpm
302+
return 0
303+
fi
304+
if [[ -x "$(node_dir)/bin/corepack" ]] && "$(node_dir)/bin/corepack" pnpm --version >/dev/null 2>&1; then
305+
set_pnpm_cmd "$(node_dir)/bin/corepack" pnpm
306+
return 0
307+
fi
308+
return 1
309+
}
310+
311+
ensure_pnpm_binary_for_scripts() {
312+
if command -v pnpm >/dev/null 2>&1; then
313+
return 0
314+
fi
315+
316+
if [[ ${#PNPM_CMD[@]} -eq 2 && "${PNPM_CMD[1]}" == "pnpm" ]] && [[ "$(basename "${PNPM_CMD[0]}")" == "corepack" ]]; then
317+
mkdir -p "${PREFIX}/bin"
318+
cat > "${PREFIX}/bin/pnpm" <<EOF
319+
#!/usr/bin/env bash
320+
set -euo pipefail
321+
exec "${PNPM_CMD[0]}" pnpm "\$@"
322+
EOF
323+
chmod +x "${PREFIX}/bin/pnpm"
324+
export PATH="${PREFIX}/bin:${PATH}"
325+
hash -r 2>/dev/null || true
326+
fi
327+
328+
if command -v pnpm >/dev/null 2>&1; then
329+
return 0
330+
fi
331+
332+
fail "pnpm command not available on PATH"
333+
}
334+
335+
run_pnpm() {
336+
if ! pnpm_cmd_is_ready; then
337+
ensure_pnpm
338+
fi
339+
"${PNPM_CMD[@]}" "$@"
340+
}
341+
283342
install_node() {
284343
local os
285344
local arch
@@ -342,9 +401,9 @@ install_node() {
342401
}
343402

344403
ensure_pnpm() {
345-
if command -v pnpm >/dev/null 2>&1; then
404+
if detect_pnpm_cmd && pnpm_cmd_is_ready; then
346405
local current_version
347-
current_version="$(pnpm --version 2>/dev/null || true)"
406+
current_version="$("${PNPM_CMD[@]}" --version 2>/dev/null || true)"
348407
if [[ "$current_version" =~ ^10\. ]]; then
349408
return 0
350409
fi
@@ -356,7 +415,7 @@ ensure_pnpm() {
356415
log "Installing pnpm via Corepack..."
357416
"$(node_dir)/bin/corepack" enable >/dev/null 2>&1 || true
358417
"$(node_dir)/bin/corepack" prepare pnpm@10 --activate
359-
if command -v pnpm >/dev/null 2>&1 && [[ "$(pnpm --version 2>/dev/null || true)" =~ ^10\. ]]; then
418+
if detect_pnpm_cmd && pnpm_cmd_is_ready && [[ "$("${PNPM_CMD[@]}" --version 2>/dev/null || true)" =~ ^10\. ]]; then
360419
emit_json "{\"event\":\"step\",\"name\":\"pnpm\",\"status\":\"ok\"}"
361420
return 0
362421
fi
@@ -365,6 +424,7 @@ ensure_pnpm() {
365424
emit_json "{\"event\":\"step\",\"name\":\"pnpm\",\"status\":\"start\",\"method\":\"npm\"}"
366425
log "Installing pnpm via npm..."
367426
SHARP_IGNORE_GLOBAL_LIBVIPS="$SHARP_IGNORE_GLOBAL_LIBVIPS" "$(npm_bin)" install -g --prefix "$PREFIX" pnpm@10
427+
detect_pnpm_cmd || true
368428
emit_json "{\"event\":\"step\",\"name\":\"pnpm\",\"status\":\"ok\"}"
369429
return 0
370430
}
@@ -435,6 +495,40 @@ EOF
435495
emit_json "{\"event\":\"step\",\"name\":\"openclaw\",\"status\":\"ok\",\"version\":\"${requested}\"}"
436496
}
437497

498+
ensure_pnpm_git_prepare_allowlist() {
499+
local repo_dir="$1"
500+
local workspace_file="${repo_dir}/pnpm-workspace.yaml"
501+
local dep="@tloncorp/api"
502+
local tmp
503+
504+
if [[ ! -f "$workspace_file" ]]; then
505+
return 0
506+
fi
507+
508+
if grep -Fq "\"${dep}\"" "$workspace_file" || grep -Fq -- "- ${dep}" "$workspace_file"; then
509+
return 0
510+
fi
511+
512+
tmp="$(mktemp)"
513+
if grep -q '^onlyBuiltDependencies:[[:space:]]*$' "$workspace_file"; then
514+
awk -v dep="$dep" '
515+
BEGIN { inserted = 0 }
516+
{
517+
print
518+
if (!inserted && $0 ~ /^onlyBuiltDependencies:[[:space:]]*$/) {
519+
print " - \"" dep "\""
520+
inserted = 1
521+
}
522+
}
523+
' "$workspace_file" >"$tmp"
524+
else
525+
cat "$workspace_file" >"$tmp"
526+
printf '\nonlyBuiltDependencies:\n - "%s"\n' "$dep" >>"$tmp"
527+
fi
528+
mv "$tmp" "$workspace_file"
529+
log "Updated pnpm allowlist for git-hosted build dependency: ${dep}"
530+
}
531+
438532
install_openclaw_from_git() {
439533
local repo_dir="$1"
440534
local repo_url="https://github.com/openclaw/openclaw.git"
@@ -457,6 +551,7 @@ install_openclaw_from_git() {
457551

458552
ensure_git
459553
ensure_pnpm
554+
ensure_pnpm_binary_for_scripts
460555

461556
if [[ -d "$repo_dir/.git" ]]; then
462557
:
@@ -479,13 +574,14 @@ install_openclaw_from_git() {
479574
fi
480575

481576
cleanup_legacy_submodules "$repo_dir"
577+
ensure_pnpm_git_prepare_allowlist "$repo_dir"
482578

483-
SHARP_IGNORE_GLOBAL_LIBVIPS="$SHARP_IGNORE_GLOBAL_LIBVIPS" pnpm -C "$repo_dir" install
579+
SHARP_IGNORE_GLOBAL_LIBVIPS="$SHARP_IGNORE_GLOBAL_LIBVIPS" run_pnpm -C "$repo_dir" install
484580

485-
if ! pnpm -C "$repo_dir" ui:build; then
581+
if ! run_pnpm -C "$repo_dir" ui:build; then
486582
log "UI build failed; continuing (CLI may still work)"
487583
fi
488-
pnpm -C "$repo_dir" build
584+
run_pnpm -C "$repo_dir" build
489585

490586
mkdir -p "${PREFIX}/bin"
491587
cat > "${PREFIX}/bin/openclaw" <<EOF
@@ -602,4 +698,6 @@ main() {
602698
fi
603699
}
604700

605-
main "$@"
701+
if [[ "${OPENCLAW_INSTALL_CLI_SH_NO_RUN:-0}" != "1" ]]; then
702+
main "$@"
703+
fi

public/install.sh

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2000,6 +2000,7 @@ install_openclaw_from_git() {
20002000
fi
20012001

20022002
cleanup_legacy_submodules "$repo_dir"
2003+
ensure_pnpm_git_prepare_allowlist "$repo_dir"
20032004

20042005
SHARP_IGNORE_GLOBAL_LIBVIPS="$SHARP_IGNORE_GLOBAL_LIBVIPS" run_quiet_step "Installing dependencies" run_pnpm -C "$repo_dir" install
20052006

@@ -2020,6 +2021,40 @@ EOF
20202021
ui_info "This checkout uses pnpm — run pnpm install (or corepack pnpm install) for deps"
20212022
}
20222023

2024+
ensure_pnpm_git_prepare_allowlist() {
2025+
local repo_dir="$1"
2026+
local workspace_file="${repo_dir}/pnpm-workspace.yaml"
2027+
local dep="@tloncorp/api"
2028+
local tmp=""
2029+
2030+
if [[ ! -f "$workspace_file" ]]; then
2031+
return 0
2032+
fi
2033+
2034+
if grep -Fq "\"${dep}\"" "$workspace_file" || grep -Fq -- "- ${dep}" "$workspace_file"; then
2035+
return 0
2036+
fi
2037+
2038+
tmp="$(mktemp)"
2039+
if grep -q '^onlyBuiltDependencies:[[:space:]]*$' "$workspace_file"; then
2040+
awk -v dep="$dep" '
2041+
BEGIN { inserted = 0 }
2042+
{
2043+
print
2044+
if (!inserted && $0 ~ /^onlyBuiltDependencies:[[:space:]]*$/) {
2045+
print " - \"" dep "\""
2046+
inserted = 1
2047+
}
2048+
}
2049+
' "$workspace_file" >"$tmp"
2050+
else
2051+
cat "$workspace_file" >"$tmp"
2052+
printf '\nonlyBuiltDependencies:\n - "%s"\n' "$dep" >>"$tmp"
2053+
fi
2054+
mv "$tmp" "$workspace_file"
2055+
ui_info "Updated pnpm allowlist for git-hosted build dependency: ${dep}"
2056+
}
2057+
20232058
# Install OpenClaw
20242059
resolve_beta_version() {
20252060
local beta=""

scripts/test-install-cli-unit.sh

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
#!/usr/bin/env bash
2+
# shellcheck disable=SC1091,SC2030,SC2031,SC2016,SC2329
3+
set -euo pipefail
4+
5+
fail() {
6+
echo "FAIL: $*" >&2
7+
exit 1
8+
}
9+
10+
assert_eq() {
11+
local got="$1"
12+
local want="$2"
13+
local msg="${3:-}"
14+
if [[ "$got" != "$want" ]]; then
15+
fail "${msg} expected=${want} got=${got}"
16+
fi
17+
}
18+
19+
assert_nonempty() {
20+
local got="$1"
21+
local msg="${2:-}"
22+
if [[ -z "$got" ]]; then
23+
fail "${msg} expected non-empty"
24+
fi
25+
}
26+
27+
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
28+
TMP_DIR="$(mktemp -d)"
29+
trap 'rm -rf "$TMP_DIR"' EXIT
30+
31+
export OPENCLAW_INSTALL_CLI_SH_NO_RUN=1
32+
# shellcheck source=../public/install-cli.sh
33+
source "${ROOT_DIR}/public/install-cli.sh"
34+
35+
echo "==> case: ensure_pnpm_binary_for_scripts installs prefix wrapper"
36+
(
37+
root="${TMP_DIR}/case-pnpm-wrapper"
38+
prefix="${root}/prefix"
39+
fake_node="${root}/node/bin"
40+
mkdir -p "${prefix}" "${fake_node}"
41+
42+
cat >"${fake_node}/corepack" <<'EOF'
43+
#!/usr/bin/env bash
44+
set -euo pipefail
45+
if [[ "${1:-}" == "pnpm" && "${2:-}" == "--version" ]]; then
46+
echo "10.29.2"
47+
exit 0
48+
fi
49+
if [[ "${1:-}" == "pnpm" ]]; then
50+
shift
51+
exec "${BASH_SOURCE%/*}/pnpm-real" "$@"
52+
fi
53+
exit 1
54+
EOF
55+
chmod +x "${fake_node}/corepack"
56+
57+
cat >"${fake_node}/pnpm-real" <<'EOF'
58+
#!/usr/bin/env bash
59+
set -euo pipefail
60+
if [[ "${1:-}" == "--version" ]]; then
61+
echo "10.29.2"
62+
exit 0
63+
fi
64+
echo "wrapped:$*"
65+
EOF
66+
chmod +x "${fake_node}/pnpm-real"
67+
68+
export PREFIX="${prefix}"
69+
export PATH="/usr/bin:/bin"
70+
set_pnpm_cmd "${fake_node}/corepack" pnpm
71+
72+
ensure_pnpm_binary_for_scripts
73+
74+
got="$(command -v pnpm || true)"
75+
assert_eq "$got" "${prefix}/bin/pnpm" "ensure_pnpm_binary_for_scripts wrapper path"
76+
out="$(pnpm --version)"
77+
assert_eq "$out" "10.29.2" "ensure_pnpm_binary_for_scripts wrapper version"
78+
)
79+
80+
echo "==> case: ensure_pnpm_git_prepare_allowlist appends known dep"
81+
(
82+
root="${TMP_DIR}/case-allowlist"
83+
repo="${root}/repo"
84+
mkdir -p "${repo}"
85+
cat >"${repo}/pnpm-workspace.yaml" <<'EOF'
86+
packages:
87+
- .
88+
89+
onlyBuiltDependencies:
90+
- esbuild
91+
EOF
92+
93+
ensure_pnpm_git_prepare_allowlist "${repo}"
94+
95+
count="$(grep -c '@tloncorp/api' "${repo}/pnpm-workspace.yaml" || true)"
96+
assert_eq "$count" "1" "ensure_pnpm_git_prepare_allowlist count"
97+
)
98+
99+
echo "==> case: install_openclaw_from_git uses run_pnpm"
100+
(
101+
root="${TMP_DIR}/case-install-git"
102+
repo="${root}/repo"
103+
prefix="${root}/prefix"
104+
home_dir="${root}/home"
105+
mkdir -p "${repo}/.git" "${repo}/dist" "${prefix}" "${home_dir}"
106+
: > "${repo}/dist/entry.js"
107+
108+
export HOME="${home_dir}"
109+
export PREFIX="${prefix}"
110+
export GIT_UPDATE=0
111+
export SHARP_IGNORE_GLOBAL_LIBVIPS=1
112+
113+
deps_cmd=""
114+
build_cmd=""
115+
116+
ensure_git() { :; }
117+
ensure_pnpm() { set_pnpm_cmd echo pnpm; }
118+
ensure_pnpm_binary_for_scripts() { :; }
119+
cleanup_legacy_submodules() { :; }
120+
log() { :; }
121+
emit_json() { :; }
122+
fail() { echo "FAIL: $*" >&2; exit 1; }
123+
run_pnpm() {
124+
if [[ -z "$deps_cmd" ]]; then
125+
deps_cmd="$1"
126+
else
127+
build_cmd="$1"
128+
fi
129+
return 0
130+
}
131+
132+
install_openclaw_from_git "${repo}"
133+
assert_eq "$deps_cmd" "-C" "install_openclaw_from_git deps command entry"
134+
assert_nonempty "$build_cmd" "install_openclaw_from_git build command entry"
135+
test -x "${prefix}/bin/openclaw"
136+
)
137+
138+
echo "PASS"

0 commit comments

Comments
 (0)