Skip to content

Commit 092d302

Browse files
authored
deploy: modularize bin shell scripts with shared helpers (#109)
1 parent abfa596 commit 092d302

16 files changed

Lines changed: 572 additions & 166 deletions

AGENTS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,8 @@ Add new test files to `vitest.config.mjs` (and shell wrappers under `test/` as n
182182
- Security functions must be pure, testable modules (no side effects, no env vars at module scope).
183183
- All security code must have tests before merging.
184184
- Run `bin/security-audit.sh --deep` after any security-relevant changes.
185+
- Keep shell CLIs thin: move reusable logic to `bin/lib/*.sh`, and source shared helpers (`shell-common.sh`, `release-common.sh`, `deploy-common.sh`, `doctor-common.sh`) instead of duplicating logging/error/root-check patterns.
186+
- For shell scripts, standardize on `bb_enable_strict_mode` and shared helper functions (`bb_log`, `bb_die`, etc.) rather than ad-hoc wrappers.
185187
- Protected files (`tool-guard.ts`, `security.mjs`, their tests) are deployed read-only. The agent cannot modify them at runtime.
186188
- New integrations get their own subdirectory (e.g. `discord-bridge/`).
187189
- Extensions are deployed from `pi/extensions/` → agent's `~/.pi/agent/extensions/`.

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,23 @@ See [SECURITY.md](SECURITY.md) for full threat model, trust boundaries, and know
162162
- [SECURITY.md](SECURITY.md) — deep security model and vulnerability reporting
163163
- [CONTRIBUTING.md](CONTRIBUTING.md) — contribution workflow
164164

165+
## Shell script architecture
166+
167+
Operational shell scripts under `bin/` follow a shared module pattern to keep command entrypoints thin and behavior consistent:
168+
169+
- shared safety/logging/error helpers in `bin/lib/shell-common.sh`
170+
- release lifecycle helpers in `bin/lib/release-common.sh`
171+
- deploy-specific helpers in `bin/lib/deploy-common.sh`
172+
- doctor output/counter helpers in `bin/lib/doctor-common.sh`
173+
- JSON parsing helpers in `bin/lib/json-common.sh`
174+
175+
Conventions:
176+
177+
- source shared modules near the top of each script
178+
- call `bb_enable_strict_mode` (strict bash mode)
179+
- prefer shared `bb_log`/`bb_die` helpers instead of ad-hoc logging/error code
180+
- keep heavy logic in `bin/lib/*` and keep CLI-facing scripts focused on orchestration
181+
165182
## Tests
166183

167184
```bash

bin/config.sh

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@
77
#
88
# Can be re-run to update existing config. Existing values shown as defaults.
99

10-
set -euo pipefail
10+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
11+
# shellcheck source=bin/lib/shell-common.sh
12+
source "$SCRIPT_DIR/lib/shell-common.sh"
13+
bb_enable_strict_mode
1114

1215
# ── Formatting ───────────────────────────────────────────────────────────────
1316

bin/deploy.sh

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,17 @@ AGENT_USER="baudbot_agent"
2020
DRY_RUN=0
2121
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
2222

23+
# shellcheck source=bin/lib/shell-common.sh
24+
source "$SCRIPT_DIR/lib/shell-common.sh"
2325
# shellcheck source=bin/lib/json-common.sh
2426
source "$SCRIPT_DIR/lib/json-common.sh"
27+
# shellcheck source=bin/lib/deploy-common.sh
28+
source "$SCRIPT_DIR/lib/deploy-common.sh"
29+
bb_enable_strict_mode
2530

2631
# Helper: run a command as baudbot_agent
2732
as_agent() {
28-
sudo -u "$AGENT_USER" "$@"
33+
bb_as_user "$AGENT_USER" "$@"
2934
}
3035

3136
for arg in "$@"; do
@@ -34,32 +39,18 @@ for arg in "$@"; do
3439
esac
3540
done
3641

42+
command -v sudo >/dev/null 2>&1 || bb_die "deploy requires sudo in PATH"
43+
[ -d "$BAUDBOT_SRC" ] || bb_die "source repo not found: $BAUDBOT_SRC"
44+
3745
# Determine admin config location (used for secret deploy + feature flags)
38-
if [ -n "${BAUDBOT_CONFIG_USER:-}" ]; then
39-
DEPLOY_USER="$BAUDBOT_CONFIG_USER"
40-
elif [ -n "${SUDO_USER:-}" ] && [ "${SUDO_USER:-}" != "root" ]; then
41-
DEPLOY_USER="$SUDO_USER"
42-
else
43-
DEPLOY_USER=$(stat -c '%U' "$BAUDBOT_SRC" 2>/dev/null || echo "")
44-
if [ -z "$DEPLOY_USER" ] || [ "$DEPLOY_USER" = "root" ]; then
45-
DEPLOY_USER="$(whoami)"
46-
fi
47-
fi
46+
DEPLOY_USER="$(bb_resolve_deploy_user "$BAUDBOT_SRC")"
4847
DEPLOY_HOME=$(getent passwd "$DEPLOY_USER" | cut -d: -f6 2>/dev/null || echo "")
4948
ADMIN_CONFIG="$DEPLOY_HOME/.baudbot/.env"
5049
RENDER_ENV_SCRIPT="$BAUDBOT_SRC/bin/render-env.sh"
5150

5251
source_env_value() {
5352
local key="$1"
54-
if [ -x "$RENDER_ENV_SCRIPT" ]; then
55-
BAUDBOT_ADMIN_HOME="$DEPLOY_HOME" BAUDBOT_CONFIG_USER="$DEPLOY_USER" "$RENDER_ENV_SCRIPT" --get "$key" 2>/dev/null || true
56-
return 0
57-
fi
58-
if [ -f "$ADMIN_CONFIG" ]; then
59-
grep -E "^${key}=" "$ADMIN_CONFIG" | tail -n 1 | cut -d= -f2- || true
60-
return 0
61-
fi
62-
return 0
53+
bb_source_env_value "$RENDER_ENV_SCRIPT" "$DEPLOY_HOME" "$DEPLOY_USER" "$ADMIN_CONFIG" "$key"
6354
}
6455

6556
EXPERIMENTAL_MODE="${BAUDBOT_EXPERIMENTAL:-}"
@@ -71,7 +62,7 @@ case "$EXPERIMENTAL_MODE" in
7162
*) EXPERIMENTAL_MODE=0 ;;
7263
esac
7364

74-
log() { echo " $1"; }
65+
log() { bb_log "$1"; }
7566

7667
# Security-critical files — deployed read-only (chmod a-w)
7768
PROTECTED_EXTENSIONS=(tool-guard.ts tool-guard.test.mjs)
@@ -112,8 +103,6 @@ if [ "$DRY_RUN" -eq 0 ]; then
112103
as_agent chmod u+w "$BAUDBOT_HOME/.pi/agent/baudbot-manifest.json" 2>/dev/null || true
113104
fi
114105

115-
set -euo pipefail
116-
117106
# ── Extensions ───────────────────────────────────────────────────────────────
118107

119108
echo "Deploying extensions..."

bin/doctor.sh

Lines changed: 22 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@
44
#
55
# Usage: baudbot doctor [--fix]
66

7-
set -euo pipefail
7+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
8+
# shellcheck source=bin/lib/shell-common.sh
9+
source "$SCRIPT_DIR/lib/shell-common.sh"
10+
# shellcheck source=bin/lib/doctor-common.sh
11+
source "$SCRIPT_DIR/lib/doctor-common.sh"
12+
bb_enable_strict_mode
813

914
for arg in "$@"; do
1015
case "$arg" in
@@ -16,17 +21,15 @@ for arg in "$@"; do
1621
done
1722

1823
BAUDBOT_HOME="/home/baudbot_agent"
19-
PASS=0
20-
FAIL=0
21-
WARN=0
24+
doctor_init_counters
2225
IS_ROOT=0
2326
if [ "$(id -u)" -eq 0 ]; then
2427
IS_ROOT=1
2528
fi
2629

27-
pass() { echo "$1"; PASS=$((PASS + 1)); }
28-
fail() { echo "$1"; FAIL=$((FAIL + 1)); }
29-
warn() { echo "$1"; WARN=$((WARN + 1)); }
30+
pass() { doctor_pass "$1"; }
31+
fail() { doctor_fail "$1"; }
32+
warn() { doctor_warn "$1"; }
3033

3134
echo "Baudbot Doctor"
3235
echo ""
@@ -103,10 +106,10 @@ echo "Admin config:"
103106

104107
# Check for admin config/env source
105108
ADMIN_USER="${SUDO_USER:-$(whoami)}"
106-
ADMIN_HOME=$(getent passwd "$ADMIN_USER" | cut -d: -f6 2>/dev/null || echo "")
109+
ADMIN_HOME="$(bb_resolve_user_home "$ADMIN_USER" || true)"
107110
ADMIN_CONFIG="$ADMIN_HOME/.baudbot/.env"
108111
BACKEND_CONF="$ADMIN_HOME/.baudbot/env-store.conf"
109-
RENDER_ENV_SCRIPT="${BAUDBOT_ROOT:-$(cd "$(dirname "$0")/.." && pwd)}/bin/render-env.sh"
112+
RENDER_ENV_SCRIPT="${BAUDBOT_ROOT:-$(cd "$SCRIPT_DIR/.." && pwd)}/bin/render-env.sh"
110113

111114
ADMIN_BACKEND="file"
112115
if [ -f "$BACKEND_CONF" ]; then
@@ -139,21 +142,10 @@ if [ -f "$ENV_FILE" ]; then
139142
fail ".env owned by $OWNER (should be baudbot_agent)"
140143
fi
141144

142-
env_value() {
143-
local key="$1"
144-
local line
145-
line=$(grep -E "^${key}=" "$ENV_FILE" 2>/dev/null | tail -n 1 || true)
146-
if [ -z "$line" ]; then
147-
echo ""
148-
return 0
149-
fi
150-
echo "${line#*=}"
151-
}
152-
153145
# LLM key validation: require at least one valid key, and flag malformed configured keys.
154146
VALID_LLM_COUNT=0
155147

156-
ANTHROPIC_VALUE="$(env_value ANTHROPIC_API_KEY)"
148+
ANTHROPIC_VALUE="$(bb_read_env_value "$ENV_FILE" ANTHROPIC_API_KEY)"
157149
if [ -n "$ANTHROPIC_VALUE" ]; then
158150
if [[ "$ANTHROPIC_VALUE" == sk-ant-* ]]; then
159151
VALID_LLM_COUNT=$((VALID_LLM_COUNT + 1))
@@ -162,7 +154,7 @@ if [ -f "$ENV_FILE" ]; then
162154
fi
163155
fi
164156

165-
OPENAI_VALUE="$(env_value OPENAI_API_KEY)"
157+
OPENAI_VALUE="$(bb_read_env_value "$ENV_FILE" OPENAI_API_KEY)"
166158
if [ -n "$OPENAI_VALUE" ]; then
167159
if [[ "$OPENAI_VALUE" == sk-* ]]; then
168160
VALID_LLM_COUNT=$((VALID_LLM_COUNT + 1))
@@ -171,12 +163,12 @@ if [ -f "$ENV_FILE" ]; then
171163
fi
172164
fi
173165

174-
GEMINI_VALUE="$(env_value GEMINI_API_KEY)"
166+
GEMINI_VALUE="$(bb_read_env_value "$ENV_FILE" GEMINI_API_KEY)"
175167
if [ -n "$GEMINI_VALUE" ]; then
176168
VALID_LLM_COUNT=$((VALID_LLM_COUNT + 1))
177169
fi
178170

179-
OPENCODE_VALUE="$(env_value OPENCODE_ZEN_API_KEY)"
171+
OPENCODE_VALUE="$(bb_read_env_value "$ENV_FILE" OPENCODE_ZEN_API_KEY)"
180172
if [ -n "$OPENCODE_VALUE" ]; then
181173
VALID_LLM_COUNT=$((VALID_LLM_COUNT + 1))
182174
fi
@@ -199,15 +191,15 @@ if [ -f "$ENV_FILE" ]; then
199191

200192
BROKER_MODE_READY=true
201193
for key in "${BROKER_REQUIRED_KEYS[@]}"; do
202-
if [ -z "$(env_value "$key")" ]; then
194+
if [ -z "$(bb_read_env_value "$ENV_FILE" "$key")" ]; then
203195
BROKER_MODE_READY=false
204196
break
205197
fi
206198
done
207199

208200
SOCKET_MODE_READY=true
209201
for key in SLACK_BOT_TOKEN SLACK_APP_TOKEN; do
210-
if [ -z "$(env_value "$key")" ]; then
202+
if [ -z "$(bb_read_env_value "$ENV_FILE" "$key")" ]; then
211203
SOCKET_MODE_READY=false
212204
break
213205
fi
@@ -216,15 +208,15 @@ if [ -f "$ENV_FILE" ]; then
216208
if [ "$BROKER_MODE_READY" = true ]; then
217209
pass "broker mode configured (SLACK_BROKER_*)"
218210
for key in SLACK_BOT_TOKEN SLACK_APP_TOKEN; do
219-
if [ -n "$(env_value "$key")" ]; then
211+
if [ -n "$(bb_read_env_value "$ENV_FILE" "$key")" ]; then
220212
pass "$key is set"
221213
else
222214
pass "$key not required in broker mode"
223215
fi
224216
done
225217
else
226218
for key in SLACK_BOT_TOKEN SLACK_APP_TOKEN; do
227-
if [ -n "$(env_value "$key")" ]; then
219+
if [ -n "$(bb_read_env_value "$ENV_FILE" "$key")" ]; then
228220
pass "$key is set"
229221
else
230222
warn "$key is not set"
@@ -357,7 +349,7 @@ fi
357349
echo ""
358350
echo "Agent:"
359351

360-
if command -v systemctl &>/dev/null && [ -d /run/systemd/system ]; then
352+
if bb_has_systemd; then
361353
enabled_state=$(systemctl is-enabled baudbot 2>&1 || true)
362354
if [ "$enabled_state" = "enabled" ]; then
363355
pass "systemd unit enabled"
@@ -432,20 +424,4 @@ fi
432424

433425
# ── Summary ──────────────────────────────────────────────────────────────────
434426

435-
echo ""
436-
echo "────────────────────────────"
437-
echo " $PASS passed, $FAIL failed, $WARN warnings"
438-
439-
if [ "$FAIL" -gt 0 ]; then
440-
echo ""
441-
echo "Fix failures before starting the agent."
442-
exit 1
443-
elif [ "$WARN" -gt 0 ]; then
444-
echo ""
445-
echo "Warnings are non-blocking but should be reviewed."
446-
exit 0
447-
else
448-
echo ""
449-
echo "All checks passed."
450-
exit 0
451-
fi
427+
doctor_summary_and_exit

0 commit comments

Comments
 (0)