From 5b4aee69b4c6bf295bc44aad6171e17a0d80e63c Mon Sep 17 00:00:00 2001 From: yyamashita Date: Sat, 11 Jul 2026 20:35:09 +0900 Subject: Dedupe Caddyfile with snippets, sync docs with reality, add tests/check.sh - Caddyfile: extract repeated header blocks into (noindex)/(crawler_bypass) snippets (adapted JSON verified byte-identical to previous config) - CLAUDE.md: add mosquitone-admin/microblog to repos table; document that proxy auth is disabled (PROXY_NO_AUTH=1) with JP-only IP filter instead - Deploy.md/caddy/README.md: reflect all-service deploy and forward proxy - tests/check.sh: shell/python syntax, repos.txt<->hooks<->sessions.txt consistency, app.py COMMANDS vs run-command.sh whitelist, secrets check Co-Authored-By: Claude Opus 4.7 --- tests/check.sh | 117 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100755 tests/check.sh (limited to 'tests') diff --git a/tests/check.sh b/tests/check.sh new file mode 100755 index 0000000..99add68 --- /dev/null +++ b/tests/check.sh @@ -0,0 +1,117 @@ +#!/usr/bin/env bash +# リポジトリの整合性チェック。push 前に実行する: bash tests/check.sh +set -uo pipefail +cd "$(dirname "$0")/.." + +FAIL=0 +ok() { echo " ok: $*"; } +ng() { echo " NG: $*"; FAIL=1; } + +echo "=== シェルスクリプト構文 ===" +while IFS= read -r f; do + if bash -n "$f" 2>/dev/null; then + ok "$f" + else + ng "$f" + bash -n "$f" || true + fi +done < <(find . -name '*.sh' -not -path './.git/*' | sort) +while IFS= read -r f; do + if bash -n "$f" 2>/dev/null; then + ok "$f" + else + ng "$f" + bash -n "$f" || true + fi +done < <(find git/hooks -type f -name 'post-receive' | sort) + +echo "" +echo "=== Python 構文 ===" +for f in hetzner-admin/app.py mail/dns/setup-route53.py; do + if python3 -m py_compile "$f" 2>/dev/null; then + ok "$f" + else + ng "$f" + python3 -m py_compile "$f" || true + fi +done + +echo "" +echo "=== repos.txt と git/hooks/ の整合性 ===" +mapfile -t repos < <(grep -vE '^\s*(#|$)' git/repos.txt | cut -d: -f1) +for repo in "${repos[@]}"; do + if [[ -f "git/hooks/$repo/post-receive" ]]; then + ok "repos.txt: $repo → hooks あり" + else + ng "repos.txt: $repo のフックがない (git/hooks/$repo/post-receive)" + fi +done +for dir in git/hooks/*/; do + name="$(basename "$dir")" + if printf '%s\n' "${repos[@]}" | grep -qx "$name"; then + ok "hooks: $name → repos.txt に定義あり" + else + ng "hooks: $name が repos.txt に定義されていない" + fi +done + +echo "" +echo "=== sessions.txt のリポジトリが repos.txt に存在するか ===" +while IFS=: read -r user repo; do + [[ "$user" =~ ^#.*$ || -z "$user" ]] && continue + if printf '%s\n' "${repos[@]}" | grep -qx "$repo"; then + ok "sessions.txt: $user:$repo" + else + ng "sessions.txt: $repo が repos.txt に定義されていない" + fi +done < claude/sessions.txt + +echo "" +echo "=== hetzner-admin: app.py の COMMANDS が run-command.sh で許可されているか ===" +while IFS= read -r cmd; do + if grep -qF "\"$cmd\")" hetzner-admin/run-command.sh; then + ok "$cmd" + else + ng "run-command.sh のホワイトリストにない: $cmd" + fi +done < <(python3 - <<'EOF' +import ast +tree = ast.parse(open("hetzner-admin/app.py").read()) +for node in ast.walk(tree): + if isinstance(node, ast.Assign) and getattr(node.targets[0], "id", "") == "COMMANDS": + for elt in node.value.elts: + print(elt.elts[1].value) +EOF +) + +echo "" +echo "=== 秘密ファイルがコミットされていないか ===" +for name in users.conf id_rsa id_rsa.pub .env; do + regex="(^|/)$(printf '%s' "$name" | sed 's/\./\\./g')\$" + if git ls-files | grep -qE "$regex"; then + ng "コミットされている: $(git ls-files | grep -E "$regex")" + else + ok "$name は未コミット" + fi +done + +echo "" +echo "=== Caddyfile 検証(docker が使える場合のみ) ===" +if docker info >/dev/null 2>&1 && docker image inspect caddy-caddy:latest >/dev/null 2>&1; then + if docker run --rm -v "$PWD/caddy:/etc/caddy:ro" caddy-caddy:latest \ + caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile >/dev/null 2>&1; then + ok "Caddyfile valid" + else + ng "Caddyfile が無効" + fi +else + echo " skip: docker にアクセスできない(root で実行すると検証される)" +fi + +echo "" +if [[ "$FAIL" -eq 0 ]]; then + echo "All checks passed." +else + echo "FAILED: 上記の NG を修正してください。" + exit 1 +fi -- cgit v1.2.3