-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpatch-all.sh
More file actions
executable file
·199 lines (170 loc) · 5.5 KB
/
patch-all.sh
File metadata and controls
executable file
·199 lines (170 loc) · 5.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
#!/bin/bash
# patch-all.sh — Orchestrator for folder-per-defect patches
# Safe to run multiple times. Each fix.py is idempotent via patch()/patch_all().
#
# Usage:
# bash patch-all.sh [--global] [--target <dir>]
#
# Options:
# --global Patch all global installs (npx cache + npm global)
# --target <dir> Patch node_modules inside <dir>
#
# If neither flag is given, --global is assumed.
set -euo pipefail
# Parse arguments
DO_GLOBAL=0
TARGET_DIR=""
while [[ $# -gt 0 ]]; do
case $1 in
--global)
DO_GLOBAL=1
shift
;;
--target)
TARGET_DIR="${2:-}"
if [[ -z "$TARGET_DIR" ]]; then
echo "Error: --target requires a directory argument"
exit 1
fi
shift 2
;;
-h|--help)
echo "Usage: patch-all.sh [--global] [--target <dir>]"
echo ""
echo "Options:"
echo " --global Patch all global installs (npx cache + npm global)"
echo " --target <dir> Patch node_modules inside <dir>"
echo ""
echo "If neither flag is given, --global is assumed."
exit 0
;;
*)
echo "Unknown option: $1"
exit 1
;;
esac
done
# Default: --global when nothing specified
if [[ $DO_GLOBAL -eq 0 && -z "$TARGET_DIR" ]]; then
DO_GLOBAL=1
fi
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# ── Shared discovery ──
. "$SCRIPT_DIR/lib/discover.sh"
# ── Collect installs ──
# Each entry: "SCOPE\tdist_src\tversion\truvector_cli\truv_swarm_root\twritable"
INSTALLS=()
if [[ $DO_GLOBAL -eq 1 ]]; then
while IFS= read -r line; do
[ -n "$line" ] && INSTALLS+=("GLOBAL $line")
done < <(discover_all_cf_installs)
fi
if [[ -n "$TARGET_DIR" ]]; then
if [[ ! -d "$TARGET_DIR" ]]; then
echo "Error: target directory does not exist: $TARGET_DIR"
exit 1
fi
TARGET_DIR="$(cd "$TARGET_DIR" && pwd)"
while IFS= read -r line; do
[ -n "$line" ] && INSTALLS+=("TARGET $line")
done < <(discover_target_installs "$TARGET_DIR")
fi
# ── Report what we found ──
TARGETS=()
if [[ $DO_GLOBAL -eq 1 ]]; then TARGETS+=(global); fi
if [[ -n "$TARGET_DIR" ]]; then TARGETS+=("$TARGET_DIR"); fi
echo "[PATCHES] Targets: ${TARGETS[*]}"
echo ""
if [[ ${#INSTALLS[@]} -eq 0 ]]; then
if [[ $DO_GLOBAL -eq 1 ]]; then
echo " Global @claude-flow/cli: not found"
echo " Global ruvector: not found"
fi
if [[ -n "$TARGET_DIR" ]]; then
echo " Target @claude-flow/cli: not found in $TARGET_DIR"
echo " Target ruvector: not found in $TARGET_DIR"
fi
echo ""
echo "[PATCHES] Complete"
exit 0
fi
for entry in "${INSTALLS[@]}"; do
IFS=$'\t' read -r scope dist_src version rv_cli rs_root writable <<< "$entry"
# "-" is the placeholder for empty fields (bash IFS collapses consecutive tabs)
[ "$rv_cli" = "-" ] && rv_cli=""
[ "$rs_root" = "-" ] && rs_root=""
echo " [$scope] @claude-flow/cli v$version at $dist_src"
[ -n "$rv_cli" ] && echo " [$scope] ruvector: $rv_cli"
[ -n "$rs_root" ] && echo " [$scope] ruv-swarm: $rs_root"
if [ "$writable" = "no" ]; then
echo " [$scope] WARNING: not writable (re-run with sudo)"
fi
done
echo ""
# ── Apply patches function ──
apply_patches() {
local base="$1"
local ruvector_cli="$2"
local ruv_swarm_root="$3"
local label="$4"
if [ -z "$base" ] && [ -z "$ruvector_cli" ]; then
echo "[$label] No packages found, skipping"
return
fi
if [ -n "$base" ]; then
echo "[$label] Patching @claude-flow/cli at: $base"
fi
if [ -n "$ruvector_cli" ]; then
echo "[$label] Patching ruvector at: $ruvector_cli"
fi
export BASE="${base:-/dev/null}"
export RUVECTOR_CLI="$ruvector_cli"
export RUV_SWARM_ROOT="$ruv_swarm_root"
# Dynamic discovery: concatenate common.py + all fix.py files sorted alphabetically.
# Alphabetical order preserves dependencies (e.g. NS-001 < NS-002 < NS-003).
#
# PATCH_INCLUDE / PATCH_EXCLUDE env vars filter by directory name regex.
python3 <(
cat "$SCRIPT_DIR/lib/common.py"
for fix in "$SCRIPT_DIR"/patch/*/fix.py; do
[ -f "$fix" ] || continue
dirname=$(basename "$(dirname "$fix")")
matchname="${dirname#[0-9][0-9][0-9]-}" # strip NNN- prefix for pattern matching
if [ -n "${PATCH_INCLUDE:-}" ] && ! echo "$matchname" | grep -qE "$PATCH_INCLUDE"; then
continue
fi
if [ -n "${PATCH_EXCLUDE:-}" ] && echo "$matchname" | grep -qE "$PATCH_EXCLUDE"; then
continue
fi
cat "$fix"
done
echo "print(f\"[$label] Done: {applied} applied, {skipped} already present\")"
)
# Shell-based patches (e.g. EM-002: transformers cache permissions)
for fix in "$SCRIPT_DIR"/patch/*/fix.sh; do
[ -f "$fix" ] || continue
dirname=$(basename "$(dirname "$fix")")
matchname="${dirname#[0-9][0-9][0-9]-}" # strip NNN- prefix for pattern matching
if [ -n "${PATCH_INCLUDE:-}" ] && ! echo "$matchname" | grep -qE "$PATCH_INCLUDE"; then
continue
fi
if [ -n "${PATCH_EXCLUDE:-}" ] && echo "$matchname" | grep -qE "$PATCH_EXCLUDE"; then
continue
fi
bash "$fix" 2>/dev/null || true
done
echo ""
}
# ── Apply to each discovered install ──
for entry in "${INSTALLS[@]}"; do
IFS=$'\t' read -r scope dist_src version rv_cli rs_root writable <<< "$entry"
[ "$rv_cli" = "-" ] && rv_cli=""
[ "$rs_root" = "-" ] && rs_root=""
if [ "$writable" = "no" ]; then
echo "[$scope] SKIP: $dist_src not writable (re-run with sudo)"
echo ""
continue
fi
apply_patches "$dist_src" "$rv_cli" "$rs_root" "$scope"
done
echo "[PATCHES] Complete"