做了一个启动脚本,原来那个不合适

This commit is contained in:
Sense T
2026-05-29 01:53:05 +00:00
parent eb00242aa4
commit fb406639e5
3 changed files with 147 additions and 40 deletions
+120
View File
@@ -0,0 +1,120 @@
#!/bin/bash
# Docker/Podman entrypoint: bootstrap config files into the mounted volume, then run hermes.
set -e
HERMES_HOME="${HERMES_HOME:-/opt/data}"
INSTALL_DIR="/opt/hermes"
# 这个脚本太烂了,我重新写一个逻辑好了
# 原本的脚本根本没有考虑用户可以自行修改目录属主的问题
if [ "$(id -u)" = "0" ]; then
groupadd -g ${HERMES_UID} runner
useradd -u ${HERMES_UID} -g ${HERMES_UID} -d ${HERMES_HOME} runner
echo "!!! Changing ${INSTALL_DIR}/.venv to ${HERMES_UID}:${HERMES_GID}"
chown -R ${HERMES_UID}:${HERMES_GID} "${INSTALL_DIR}/.venv" || exit 1
# Drop root privilege
echo "Dropping root privileges"
exec gosu ${HERMES_UID} "$0" "$@"
fi
# --- Running as hermes from here ---
source "${INSTALL_DIR}/.venv/bin/activate"
# 这狗*的环境变量!
export PATH=${HERMES_HOME}/home/.local/bin:${HERMES_HOME}/.local/bin:${INSTALL_DIR}/bin:${PATH}
# Create essential directory structure. Cache and platform directories
# (cache/images, cache/audio, platforms/whatsapp, etc.) are created on
# demand by the application — don't pre-create them here so new installs
# get the consolidated layout from get_hermes_dir().
# The "home/" subdirectory is a per-profile HOME for subprocesses (git,
# ssh, gh, npm …). Without it those tools write to /root which is
# ephemeral and shared across profiles. See issue #4426.
mkdir -p "$HERMES_HOME"/{cron,sessions,logs,hooks,memories,skills,skins,plans,workspace,home}
# .env
if [ ! -f "$HERMES_HOME/.env" ]; then
cp "$INSTALL_DIR/.env.example" "$HERMES_HOME/.env"
fi
# config.yaml
if [ ! -f "$HERMES_HOME/config.yaml" ]; then
cp "$INSTALL_DIR/cli-config.yaml.example" "$HERMES_HOME/config.yaml"
fi
# SOUL.md
if [ ! -f "$HERMES_HOME/SOUL.md" ]; then
cp "$INSTALL_DIR/docker/SOUL.md" "$HERMES_HOME/SOUL.md"
fi
# auth.json: bootstrap from env on first boot only. Used by orchestrators
# (e.g. provisioning a Hermes VPS from an account-management service) that
# need to seed the OAuth refresh credential non-interactively, instead of
# walking the user through `hermes setup` + the device-flow login dance.
# Subsequent token rotations write back to the same file, which lives on a
# persistent volume — so this env var is consumed exactly once at first
# boot. The `[ ! -f ... ]` guard is critical: without it, a container
# restart would clobber a rotated refresh token with the now-stale value
# the orchestrator originally seeded.
if [ ! -f "$HERMES_HOME/auth.json" ] && [ -n "$HERMES_AUTH_JSON_BOOTSTRAP" ]; then
printf '%s' "$HERMES_AUTH_JSON_BOOTSTRAP" > "$HERMES_HOME/auth.json"
chmod 600 "$HERMES_HOME/auth.json"
fi
# Sync bundled skills (manifest-based so user edits are preserved)
if [ -d "$INSTALL_DIR/skills" ]; then
python3 "$INSTALL_DIR/tools/skills_sync.py"
fi
# Optionally start `hermes dashboard` as a side-process.
#
# Toggled by HERMES_DASHBOARD=1 (also accepts "true"/"yes", case-insensitive).
# Host/port/TUI can be overridden via:
# HERMES_DASHBOARD_HOST (default 0.0.0.0 — exposed outside the container)
# HERMES_DASHBOARD_PORT (default 9119, matches `hermes dashboard` default)
# HERMES_DASHBOARD_TUI (already honored by `hermes dashboard` itself)
#
# The dashboard is a long-lived server. We background it *before* the final
# `exec hermes "$@"` so the user's chosen foreground command (chat, gateway,
# sleep infinity, …) remains PID-of-interest for the container runtime. When
# the container stops the whole process tree is torn down, so no explicit
# cleanup is needed.
case "${HERMES_DASHBOARD:-}" in
1|true|TRUE|True|yes|YES|Yes)
dash_host="${HERMES_DASHBOARD_HOST:-0.0.0.0}"
dash_port="${HERMES_DASHBOARD_PORT:-9119}"
dash_args=(--host "$dash_host" --port "$dash_port" --no-open)
# Binding to anything other than localhost requires --insecure — the
# dashboard refuses otherwise because it exposes API keys. Inside a
# container this is the expected deployment (host reaches it via
# published port), so opt in automatically.
if [ "$dash_host" != "127.0.0.1" ] && [ "$dash_host" != "localhost" ]; then
dash_args+=(--insecure)
fi
echo "Starting hermes dashboard on ${dash_host}:${dash_port} (background)"
# Prefix dashboard output so it's distinguishable from the main
# process in `docker logs`. stdbuf keeps the pipe line-buffered.
(
stdbuf -oL -eL hermes dashboard "${dash_args[@]}" 2>&1 \
| sed -u 's/^/[dashboard] /'
) &
;;
esac
# Final exec: two supported invocation patterns.
#
# docker run <image> -> exec `hermes` with no args (legacy default)
# docker run <image> chat -q "..." -> exec `hermes chat -q "..."` (legacy wrap)
# docker run <image> sleep infinity -> exec `sleep infinity` directly
# docker run <image> bash -> exec `bash` directly
#
# If the first positional arg resolves to an executable on PATH, we assume the
# caller wants to run it directly (needed by the launcher which runs long-lived
# `sleep infinity` sandbox containers — see tools/environments/docker.py).
# Otherwise we treat the args as a hermes subcommand and wrap with `hermes`,
# preserving the documented `docker run <image> <subcommand>` behavior.
if [ $# -gt 0 ] && command -v "$1" >/dev/null 2>&1; then
exec "$@"
fi
exec hermes "$@"
+20 -40
View File
@@ -4,7 +4,6 @@ metadata:
name: hermes
namespace: hermes
spec:
strategy:
type: Recreate
replicas: 1
@@ -16,10 +15,6 @@ spec:
labels:
app: hermes
spec:
securityContext:
runAsUser: 10000
supplementalGroups:
- 1000
volumes:
- name: data
hostPath:
@@ -35,6 +30,10 @@ spec:
type: DirectoryOrCreate
- name: tmp
emptyDir: {}
- name: start-sh
configMap:
name: hermes-start
defaultMode: 0555
containers:
- name: gateway
#image: cr.wetofu.me/nousresearch/hermes-agent:v2026.5.16
@@ -43,7 +42,7 @@ spec:
httpGet:
path: /health
port: 8642
initialDelaySeconds: 30
initialDelaySeconds: 60
periodSeconds: 10
successThreshold: 1
failureThreshold: 3
@@ -58,6 +57,8 @@ spec:
ports:
- containerPort: 8642
name: gateway
- containerPort: 9119
name: dashboard
args:
- gateway
- run
@@ -70,6 +71,16 @@ spec:
value: "0.0.0.0"
- name: API_SERVER_CORS_ORIGINS
value: '*'
- name: HERMES_UID
value: '1000'
- name: HERMES_GID
value: '1000'
- name: HERMES_DASHBOARD
value: 'true'
- name: HERMES_DASHBOARD_HOST
value: '::'
- name: HERMES_DASHBOARD_HOST
value: '9119'
envFrom:
- secretRef:
name: hermes
@@ -89,6 +100,9 @@ spec:
mountPath: /opt/data/workspace/Projects
- name: tmp
mountPath: /tmp
- name: start-sh
mountPath: /opt/hermes/docker/entrypoint.sh
subPath: entrypoint.sh
resources:
requests:
memory: "1Gi"
@@ -96,37 +110,3 @@ spec:
limits:
memory: "4Gi"
cpu: "2"
- name: dashboard
image: image
args:
- dashboard
ports:
- containerPort: 9119
name: dashboard
env:
- name: TZ
value: Asia/Shanghai
- name: GATEWAY_HEALTH_URL
value: localhost:8642
- name: GATEWAY_HEALTH_TIMEOUT
value: "3"
- name: HERMES_DASHBOARD_HOST
value: "::"
envFrom:
- secretRef:
name: hermes
optional: true
- configMapRef:
name: hermes
volumeMounts:
- name: data
mountPath: /opt/data
- name: tmp
mountPath: /tmp
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
+7
View File
@@ -1,6 +1,9 @@
# yaml-language-server: $schema=https://json.schemastore.org/kustomization.json
kind: Kustomization
namespace: hermes
replicas:
- name: hermes
count: 1
resources:
- deploy.yaml
- services.yaml
@@ -25,3 +28,7 @@ configMapGenerator:
- config/TELEGRAM_OBSERVE_UNMENTIONED_GROUP_MESSAGES
- config/TELEGRAM_REQUIRE_MENTION
- config/SIYUAN_URL
- config/PATH
- name: hermes-start
files:
- config/entrypoint.sh