-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinstall-interface.sh
More file actions
executable file
·202 lines (179 loc) · 8.08 KB
/
Copy pathinstall-interface.sh
File metadata and controls
executable file
·202 lines (179 loc) · 8.08 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
200
201
202
#!/usr/bin/env bash
#
# install-interface.sh -- install the released SmartMeshCoreInterface.py (the
# main branch on GitHub) into a Reticulum
# install's interfaces/ directory, and make sure the `meshcore` python library
# it needs is installed for the python that runs rnsd. Run it straight from
# GitHub:
#
# curl -fsSL https://raw.githubusercontent.com/afit21/Reticulum-Smart-MeshCore-Interface/main/install-interface.sh | bash
#
# or download it and run ./install-interface.sh; --help lists the options.
# dev-tools/install-interface-dev.sh is the same script for the development branch.
#
set -euo pipefail
usage() {
cat <<'USAGE'
install-interface.sh -- install SmartMeshCoreInterface.py from GitHub (main
branch) into a Reticulum install's interfaces/ directory.
Defaults to the main branch and ~/.reticulum. The downloaded file is checked
before it replaces anything: a truncated download, an HTML error page or a
Python syntax error leaves the installed interface untouched, because a broken
interface file stops rnsd/MeshChat from starting at all. The interface imports
the `meshcore` python library at startup and panics rnsd without it, so the
script also makes sure that library is importable by the python that runs
rnsd, installing it with pip if it is not.
./install-interface.sh # main -> ~/.reticulum
./install-interface.sh --branch development # (or use dev-tools/install-interface-dev.sh)
./install-interface.sh --config-dir ~/.reticulum_test
./install-interface.sh --check # report only, install nothing
./install-interface.sh --force # reinstall even if unchanged
./install-interface.sh --skip-deps # don't check/install the meshcore library
Straight from GitHub (options go after `bash -s --`):
curl -fsSL https://raw.githubusercontent.com/afit21/Reticulum-Smart-MeshCore-Interface/main/install-interface.sh | bash
curl -fsSL https://raw.githubusercontent.com/afit21/Reticulum-Smart-MeshCore-Interface/main/install-interface.sh | bash -s -- --check
USAGE
}
REPO="afit21/Reticulum-Smart-MeshCore-Interface"
BRANCH="main"
SRC_PATH="Interface/SmartMeshCoreInterface.py"
CONFIG_DIR="${RNS_CONFIG_DIR:-$HOME/.reticulum}"
KEEP_BACKUPS=5
CHECK_ONLY=0
FORCE=0
SKIP_DEPS=0
die() { printf '\033[31merror:\033[0m %s\n' "$*" >&2; exit 1; }
info() { printf '\033[36m==>\033[0m %s\n' "$*"; }
ok() { printf '\033[32m ok:\033[0m %s\n' "$*"; }
warn() { printf '\033[33mwarn:\033[0m %s\n' "$*" >&2; }
while [ $# -gt 0 ]; do
case "$1" in
-b|--branch) BRANCH="${2:?--branch needs a value}"; shift 2 ;;
-d|--config-dir) CONFIG_DIR="${2:?--config-dir needs a value}"; shift 2 ;;
-c|--check) CHECK_ONLY=1; shift ;;
-f|--force) FORCE=1; shift ;;
-s|--skip-deps) SKIP_DEPS=1; shift ;;
-h|--help) usage; exit 0 ;;
*) die "unknown option: $1 (try --help)" ;;
esac
done
command -v curl >/dev/null || die "curl is not installed"
PYTHON="$(command -v python3 || true)"
[ -n "$PYTHON" ] || die "python3 is not installed (needed to syntax-check the download)"
# --- the meshcore python library --------------------------------------------
# The interface does `import meshcore` when rnsd loads it and panics rnsd if
# that fails, so check the python that actually runs rnsd (its shebang), not
# just whichever python3 is first on PATH -- a pipx or venv rnsd has its own.
RNSD_PY="$PYTHON"
if command -v rnsd >/dev/null 2>&1; then
shebang="$(head -n1 "$(command -v rnsd)" 2>/dev/null || true)"
case "$shebang" in
'#!'*)
cand="$(printf '%s\n' "${shebang#\#!}" | awk '{ if ($1 ~ /\/env$/) print $2; else print $1 }')"
case "$cand" in
/*) [ -x "$cand" ] && RNSD_PY="$cand" ;;
?*) cand="$(command -v "$cand" 2>/dev/null || true)"; [ -n "$cand" ] && RNSD_PY="$cand" ;;
esac ;;
esac
fi
meshcore_version() {
"$RNSD_PY" -c 'import importlib.metadata as m; print(m.version("meshcore"))' 2>/dev/null || echo "version unknown"
}
ensure_meshcore() {
if "$RNSD_PY" -c 'import meshcore' >/dev/null 2>&1; then
ok "meshcore library $(meshcore_version) importable by $RNSD_PY"
return 0
fi
if [ "$CHECK_ONLY" -eq 1 ]; then
warn "the meshcore python library is NOT importable by $RNSD_PY -- the interface panics rnsd without it"
return 0
fi
info "meshcore library not importable by $RNSD_PY -- installing it"
case "$RNSD_PY" in
*/pipx/venvs/*)
command -v pipx >/dev/null 2>&1 && pipx inject rns meshcore >/dev/null 2>&1 || true ;;
esac
"$RNSD_PY" -c 'import meshcore' >/dev/null 2>&1 \
|| "$RNSD_PY" -m pip install --quiet meshcore >/dev/null 2>&1 \
|| "$RNSD_PY" -m pip install --quiet --user meshcore >/dev/null 2>&1 \
|| true
"$RNSD_PY" -c 'import meshcore' >/dev/null 2>&1 \
|| die "could not install the meshcore library for $RNSD_PY -- install it by hand ('$RNSD_PY -m pip install meshcore', or 'pipx inject rns meshcore' for a pipx rnsd) and re-run"
ok "meshcore library $(meshcore_version) installed for $RNSD_PY"
}
if [ "$SKIP_DEPS" -eq 1 ]; then
info "--skip-deps given: not checking the meshcore library"
else
ensure_meshcore
fi
DEST_DIR="$CONFIG_DIR/interfaces"
DEST="$DEST_DIR/SmartMeshCoreInterface.py"
URL="https://raw.githubusercontent.com/$REPO/$BRANCH/$SRC_PATH"
[ -d "$CONFIG_DIR" ] || die "config dir does not exist: $CONFIG_DIR (use --config-dir)"
mkdir -p "$DEST_DIR"
TMP="$(mktemp -t SmartMeshCoreInterface.XXXXXX.py)"
trap 'rm -f "$TMP"' EXIT
info "fetching $BRANCH from github.com/$REPO"
# --fail so a 404 (bad branch/path) is an error, not a saved error page.
curl -fsSL --retry 3 --retry-delay 2 --connect-timeout 15 -o "$TMP" "$URL" \
|| die "download failed: $URL (does branch '$BRANCH' exist?)"
# --- sanity checks on what we actually got -------------------------------
bytes=$(wc -c < "$TMP" | tr -d ' ')
[ "$bytes" -ge 100000 ] || die "downloaded file is only ${bytes} bytes -- truncated or not the interface"
grep -q "class SmartMeshCoreInterface" "$TMP" \
|| die "downloaded file does not define SmartMeshCoreInterface -- wrong path or an error page"
"$PYTHON" -m py_compile "$TMP" 2>/dev/null \
|| die "downloaded file is not valid Python -- refusing to install it"
rm -rf "$(dirname "$TMP")/__pycache__" 2>/dev/null || true
ok "${bytes} bytes, defines SmartMeshCoreInterface, compiles"
new_sum=$("$PYTHON" -c "import hashlib,sys;print(hashlib.sha256(open(sys.argv[1],'rb').read()).hexdigest())" "$TMP")
if [ -f "$DEST" ]; then
old_sum=$("$PYTHON" -c "import hashlib,sys;print(hashlib.sha256(open(sys.argv[1],'rb').read()).hexdigest())" "$DEST")
old_lines=$(wc -l < "$DEST" | tr -d ' ')
new_lines=$(wc -l < "$TMP" | tr -d ' ')
info "installed: ${old_lines} lines (${old_sum:0:12})"
info "on $BRANCH: ${new_lines} lines (${new_sum:0:12})"
if [ "$old_sum" = "$new_sum" ]; then
if [ "$FORCE" -eq 1 ]; then
warn "identical to what is installed, but --force given"
else
ok "already up to date -- nothing to do"
exit 0
fi
fi
else
info "no interface installed yet at $DEST"
fi
if [ "$CHECK_ONLY" -eq 1 ]; then
info "--check given: not installing"
if [ -f "$DEST" ] && command -v diff >/dev/null; then
diff -u "$DEST" "$TMP" | head -60 || true
echo "(diff truncated to 60 lines)"
fi
exit 0
fi
# --- install, keeping a timestamped backup -------------------------------
if [ -f "$DEST" ]; then
backup="$DEST.bak.$(date +%Y%m%dT%H%M%S)"
cp -p "$DEST" "$backup"
ok "backed up to $(basename "$backup")"
# Keep only the newest $KEEP_BACKUPS backups.
ls -1t "$DEST".bak.* 2>/dev/null | tail -n +$((KEEP_BACKUPS + 1)) | while read -r stale; do
rm -f "$stale"
done
fi
install -m 0644 "$TMP" "$DEST"
ok "installed $DEST"
# --- who needs restarting for this to take effect? ----------------------
running=""
for pat in rnsd nomadnet meshchat MeshChat lxmd; do
if pgrep -f "$pat" >/dev/null 2>&1; then
running="$running $pat"
fi
done
if [ -n "$running" ]; then
warn "Reticulum loads this file at startup -- restart to pick it up:$running"
else
info "no rnsd/MeshChat/NomadNet process seen; it will load on next start"
fi
printf '\033[32mdone:\033[0m %s @ %s -> %s\n' "$BRANCH" "${new_sum:0:12}" "$DEST"