-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbootstrap.sh
More file actions
executable file
·151 lines (141 loc) · 5.72 KB
/
bootstrap.sh
File metadata and controls
executable file
·151 lines (141 loc) · 5.72 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
#!/usr/bin/env bash
# One-shot bootstrap that imports an existing OpenClaw home volume into
# a fresh Hermes home volume.
#
# Run once after replacing an OpenClaw deployment with the Hermes stack
# in compose.yml. The legacy openclaw-home volume from the previous
# deployment is mounted read-only; the migration target is the new
# hermes-home volume created by compose.yml.
#
# Re-running this script overwrites Hermes-side files for any item that
# would otherwise conflict, because we pass --overwrite to
# `hermes claw migrate`. That is appropriate for the initial migration
# (we want OpenClaw's persona, model config, and provider keys to
# replace the bundled Hermes defaults that the entrypoint copies in on
# first boot) but does mean a re-run will clobber edits the operator
# made on the Hermes side after the first import. Set
# BOOTSTRAP_OVERWRITE=0 to drop the flag for a strictly additive
# re-run; the migrate command will then fail loudly on conflicts so
# they can be reviewed.
#
# Usage:
# bash bootstrap.sh [project-name] [legacy-volume]
#
# project-name docker compose project (defaults to the basename of
# the current directory, e.g. "hermes")
# legacy-volume full name of the openclaw-home volume from the prior
# deployment (defaults to sluice_openclaw-home which is
# the historical default; pass an explicit name if your
# prior project was named differently).
#
# What it does:
# 1. Stage the legacy openclaw home under a path that does not match
# `pgrep -f openclaw` so the migrate command's running-instance
# check does not false-positive on its own argv. Chown to the
# hermes UID so the in-container migrator can read it.
# 2. Run `hermes claw migrate --preset full --migrate-secrets
# --overwrite --no-backup --yes` against the staged source.
# 3. Patch mcp_servers.sluice.url into ~/.hermes/config.yaml so
# Hermes registers sluice as an MCP server. Sluice's
# HermesProfile.WireMCPCmd does this from the running sluice
# container, but having a one-shot pre-write is helpful when
# rebootstrapping a stack from clean.
# 4. Clean up the staging directory.
set -euo pipefail
PROJECT_NAME="${1:-$(basename "$PWD")}"
LEGACY_VOLUME="${2:-sluice_openclaw-home}"
HERMES_VOLUME="${PROJECT_NAME}_hermes-home"
HERMES_IMAGE="${HERMES_IMAGE:-nousresearch/hermes-agent:v2026.4.30}"
BOOTSTRAP_OVERWRITE="${BOOTSTRAP_OVERWRITE:-1}"
# Honor compose.yml's HERMES_UID/HERMES_GID overrides so files chowned
# by this bootstrap stay readable when the operator pinned the runtime
# user to a non-default UID/GID. Defaults match the hermes user baked
# into the upstream image (UID/GID 10000).
HERMES_UID="${HERMES_UID:-10000}"
HERMES_GID="${HERMES_GID:-10000}"
migrate_args=(
--source /opt/data/.hermes/.migration-source
--preset full
--migrate-secrets
--no-backup
--yes
)
if [ "$BOOTSTRAP_OVERWRITE" = "1" ]; then
migrate_args+=(--overwrite)
fi
if ! docker volume inspect "$LEGACY_VOLUME" >/dev/null 2>&1; then
echo "==> legacy volume '$LEGACY_VOLUME' not found; skipping bootstrap (fresh install)"
exit 0
fi
echo "==> ensuring hermes-home volume exists"
docker volume create "$HERMES_VOLUME" >/dev/null
echo "==> staging legacy data (path renamed to avoid pgrep -f openclaw false-positive)"
docker run --rm \
--user 0:0 \
-e HERMES_UID="$HERMES_UID" \
-e HERMES_GID="$HERMES_GID" \
-v "$LEGACY_VOLUME":/legacy:ro \
-v "$HERMES_VOLUME":/opt/data/.hermes \
--entrypoint /bin/bash \
"$HERMES_IMAGE" -c '
set -e
rm -rf /opt/data/.hermes/.migration-source
cp -a /legacy/.openclaw /opt/data/.hermes/.migration-source
chown -R "$HERMES_UID:$HERMES_GID" /opt/data/.hermes/.migration-source
'
echo "==> running hermes claw migrate (overwrite=$BOOTSTRAP_OVERWRITE)"
docker run --rm \
-e HERMES_HOME=/opt/data/.hermes \
-e HERMES_UID="$HERMES_UID" \
-e HERMES_GID="$HERMES_GID" \
-v "$HERMES_VOLUME":/opt/data/.hermes \
"$HERMES_IMAGE" \
claw migrate "${migrate_args[@]}" \
|| echo "migrate exited non-zero (non-fatal warnings or refused on conflicts) -- continuing"
echo "==> patching mcp_servers.sluice.url into config.yaml"
docker run --rm \
-e HERMES_HOME=/opt/data/.hermes \
-e HERMES_UID="$HERMES_UID" \
-e HERMES_GID="$HERMES_GID" \
-v "$HERMES_VOLUME":/opt/data/.hermes \
--entrypoint /bin/bash \
"$HERMES_IMAGE" -c '
set -e
# Activate the bundled venv when present (PyYAML lives there on the
# official Hermes image). Fall back to system python3 otherwise so
# this script also works against custom Hermes builds that omit the
# venv. Mirrors the runtime wrapper in HermesProfile.WireMCPCmd.
if [ -f /opt/hermes/.venv/bin/activate ]; then
. /opt/hermes/.venv/bin/activate
fi
if ! python3 -c "import yaml" 2>/dev/null; then
echo "error: python3 PyYAML is required but not available; install it or use an image that bundles it" >&2
exit 1
fi
python3 - <<PY
import os, yaml
p = "/opt/data/.hermes/config.yaml"
data = {}
if os.path.exists(p):
with open(p) as fh:
data = yaml.safe_load(fh) or {}
servers = data.setdefault("mcp_servers", {})
existing = servers.get("sluice") or {}
if existing.get("url") != "http://sluice:3000/mcp":
existing["url"] = "http://sluice:3000/mcp"
servers["sluice"] = existing
with open(p, "w") as fh:
yaml.safe_dump(data, fh, sort_keys=False)
print("patched", p)
else:
print("already patched")
PY
chown "$HERMES_UID:$HERMES_GID" /opt/data/.hermes/config.yaml
'
echo "==> cleaning up staged migration source"
docker run --rm \
--user 0:0 \
-v "$HERMES_VOLUME":/opt/data/.hermes \
--entrypoint /bin/bash \
"$HERMES_IMAGE" -c 'rm -rf /opt/data/.hermes/.migration-source'
echo "==> bootstrap complete"