From 05e1418efa56a773c295a2e072796e220d8d8f51 Mon Sep 17 00:00:00 2001 From: yyamashita Date: Sat, 31 Oct 2026 22:45:58 +0900 Subject: hetzner-admin: stop/start individual services, shown as STOPPED on status page - run-command.sh: services / stop / start for app and infra stacks (not caddy, hetzner-admin, status). Stopped names go to status/stopped.txt (gitignored). - status: a stopped service that does not respond is STOPPED (gray), excluded from uptime and DOWN alerts. If it responds it is UP as usual. - Infra deploy.sh skips stopped stacks; app repo pushes clear the flag. - claude-code@.service: Restart=on-failure, and restart-sessions.sh restarts one unit at a time, to avoid the tmux "server exited unexpectedly" race. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01N5eS3U54gzjRXzm65Np1RS --- .gitignore | 1 + cgit/deploy.sh | 6 +++ claude/restart-sessions.sh | 2 +- claude/systemd/claude-code@.service | 4 ++ git/hooks/microblog/post-receive | 2 + git/hooks/todo/post-receive | 2 + git/hooks/tokyo-livehouse-events/post-receive | 2 + git/hooks/whois-band/post-receive | 2 + hetzner-admin/app.py | 57 ++++++++++++++++++++++++++- hetzner-admin/run-command.sh | 41 +++++++++++++++++++ mail/deploy.sh | 6 +++ ntfy/deploy.sh | 6 +++ status/app.py | 57 ++++++++++++++++++++------- tests/check.sh | 26 ++++++++++++ vpn/deploy.sh | 6 +++ 15 files changed, 203 insertions(+), 17 deletions(-) diff --git a/.gitignore b/.gitignore index 89e2e94..eae39f2 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ hetzner-admin/id_rsa hetzner-admin/id_rsa.pub __pycache__/ hetzner-admin/data/ +status/stopped.txt diff --git a/cgit/deploy.sh b/cgit/deploy.sh index 276b188..eecf5f9 100644 --- a/cgit/deploy.sh +++ b/cgit/deploy.sh @@ -1,4 +1,10 @@ #!/usr/bin/env bash set -euo pipefail cd "$(dirname "$0")" + +# 管理画面で停止中なら起動しない(Start で stopped.txt から外れる) +if grep -qx git ../status/stopped.txt 2>/dev/null; then + echo "cgit: 管理画面で停止中のためスキップ" + exit 0 +fi docker compose up -d --build diff --git a/claude/restart-sessions.sh b/claude/restart-sessions.sh index 5f0e1b4..3e1c495 100644 --- a/claude/restart-sessions.sh +++ b/claude/restart-sessions.sh @@ -3,4 +3,4 @@ set -e uid=$(id -u yyamashita) runuser -u yyamashita -- \ env XDG_RUNTIME_DIR=/run/user/$uid DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$uid/bus \ - bash -c 'systemctl --user list-units --plain --no-legend "claude-code@*.service" | awk "{print \$1}" | xargs -r systemctl --user restart' + bash -c 'systemctl --user list-units --plain --no-legend "claude-code@*.service" | awk "{print \$1}" | xargs -r -n1 systemctl --user restart' diff --git a/claude/systemd/claude-code@.service b/claude/systemd/claude-code@.service index e85507e..7ce0e03 100644 --- a/claude/systemd/claude-code@.service +++ b/claude/systemd/claude-code@.service @@ -8,6 +8,10 @@ RemainAfterExit=yes WorkingDirectory=/home/yyamashita/workspaces/repos/%i ExecStart=/usr/bin/tmux new-session -d -s claude-%i -c /home/yyamashita/workspaces/repos/%i /usr/local/bin/claude --remote-control %i --dangerously-skip-permissions ExecStop=-/usr/bin/tmux kill-session -t claude-%i +# 全セッション同時に再起動すると、tmux サーバーが終了する瞬間に new-session が繋がり +# "server exited unexpectedly" で失敗することがあるので、少し待って再試行する +Restart=on-failure +RestartSec=3 Environment=HOME=/home/yyamashita Environment=USER=yyamashita Environment=XDG_RUNTIME_DIR=/run/user/1000 diff --git a/git/hooks/microblog/post-receive b/git/hooks/microblog/post-receive index 2627861..87d51cc 100755 --- a/git/hooks/microblog/post-receive +++ b/git/hooks/microblog/post-receive @@ -4,5 +4,7 @@ APP_DIR=/app/microblog GIT_WORK_TREE=$APP_DIR git checkout -f cd $APP_DIR mkdir -p data +# push でデプロイしたら管理画面の停止状態を解除する +sed -i '/^blog$/d' /app/infra/status/stopped.txt 2>/dev/null || true docker compose up -d --build echo "Deploy complete: microblog" diff --git a/git/hooks/todo/post-receive b/git/hooks/todo/post-receive index 645b856..0952ee5 100644 --- a/git/hooks/todo/post-receive +++ b/git/hooks/todo/post-receive @@ -4,5 +4,7 @@ APP_DIR=/app/todo GIT_WORK_TREE=$APP_DIR git checkout -f cd $APP_DIR mkdir -p data +# push でデプロイしたら管理画面の停止状態を解除する +sed -i '/^todo$/d' /app/infra/status/stopped.txt 2>/dev/null || true docker compose up -d --build echo "Deploy complete: todo" diff --git a/git/hooks/tokyo-livehouse-events/post-receive b/git/hooks/tokyo-livehouse-events/post-receive index ca64767..6a80e28 100755 --- a/git/hooks/tokyo-livehouse-events/post-receive +++ b/git/hooks/tokyo-livehouse-events/post-receive @@ -4,5 +4,7 @@ APP_DIR=/app/tokyo-livehouse-events GIT_WORK_TREE=$APP_DIR git checkout -f cd $APP_DIR mkdir -p data +# push でデプロイしたら管理画面の停止状態を解除する +sed -i '/^golive$/d' /app/infra/status/stopped.txt 2>/dev/null || true docker compose up -d --build echo "Deploy complete: tokyo-livehouse-events" diff --git a/git/hooks/whois-band/post-receive b/git/hooks/whois-band/post-receive index 60b368f..de4d7c7 100755 --- a/git/hooks/whois-band/post-receive +++ b/git/hooks/whois-band/post-receive @@ -4,5 +4,7 @@ APP_DIR=/app/whois-band GIT_WORK_TREE=$APP_DIR git checkout -f cd $APP_DIR mkdir -p data +# push でデプロイしたら管理画面の停止状態を解除する +sed -i '/^whoisband$/d' /app/infra/status/stopped.txt 2>/dev/null || true docker compose up -d --build echo "Deploy complete: whois-band" diff --git a/hetzner-admin/app.py b/hetzner-admin/app.py index 90a1cca..ac3178c 100644 --- a/hetzner-admin/app.py +++ b/hetzner-admin/app.py @@ -65,6 +65,9 @@ COMMANDS = [ COMMAND_MAP = {label: cmd for label, cmd in COMMANDS} +# 個別に停止・起動できるサービス(run-command.sh の SERVICES と status/targets.txt の名前に合わせる) +SERVICES = ["golive", "whoisband", "blog", "todo", "git", "vpn", "ntfy", "mail"] + # ブラウザ側: JSON ⇔ WebAuthn の変換はブラウザ組み込みの # parseCreationOptionsFromJSON / parseRequestOptionsFromJSON / toJSON に任せる PASSKEY_JS = """