300 lines
11 KiB
Bash
Executable File
300 lines
11 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
#
|
||
# MAESTRO 初回セットアップスクリプト
|
||
#
|
||
# 新しい環境でゼロから動かすための対話式セットアップ。
|
||
# クローン済みのディレクトリで実行する:
|
||
#
|
||
# git clone https://gitea.example.com/your-org/maestro.git maestro
|
||
# cd maestro
|
||
# ./scripts/setup.sh
|
||
#
|
||
set -euo pipefail
|
||
|
||
PROJECT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||
cd "$PROJECT_DIR"
|
||
|
||
echo "=========================================="
|
||
echo " MAESTRO セットアップ"
|
||
echo "=========================================="
|
||
echo ""
|
||
|
||
# -------------------------------------------------------
|
||
# Step 1: 依存関係チェック
|
||
# -------------------------------------------------------
|
||
echo "[1/5] 前提条件チェック..."
|
||
|
||
MISSING=()
|
||
command -v node >/dev/null 2>&1 || MISSING+=("node")
|
||
command -v npm >/dev/null 2>&1 || MISSING+=("npm")
|
||
command -v curl >/dev/null 2>&1 || MISSING+=("curl")
|
||
|
||
if [ ${#MISSING[@]} -gt 0 ]; then
|
||
echo " ERROR: 以下がインストールされていません: ${MISSING[*]}"
|
||
exit 1
|
||
fi
|
||
|
||
NODE_VER=$(node -v | sed 's/v//' | cut -d. -f1)
|
||
if [ "$NODE_VER" -lt 20 ]; then
|
||
echo " ERROR: Node.js 20+ が必要です(現在: $(node -v))"
|
||
exit 1
|
||
fi
|
||
|
||
echo " Node.js $(node -v) ... OK"
|
||
echo ""
|
||
|
||
# -------------------------------------------------------
|
||
# Step 2: npm install & build
|
||
# -------------------------------------------------------
|
||
echo "[2/5] 依存パッケージのインストール & ビルド..."
|
||
|
||
./scripts/build-all.sh | tail -5
|
||
|
||
echo " ビルド完了"
|
||
echo ""
|
||
|
||
# Playwright ブラウザのインストール(WebSearch に必要)
|
||
echo " Playwright ブラウザをインストール中..."
|
||
if npx playwright install chromium 2>/dev/null; then
|
||
echo " Playwright chromium インストール完了"
|
||
else
|
||
echo " WARN: Playwright のインストールに失敗しました。WebSearch は SearXNG フォールバックで動作します。"
|
||
echo " 後から 'npx playwright install chromium' で手動インストールできます。"
|
||
fi
|
||
echo ""
|
||
|
||
# -------------------------------------------------------
|
||
# Step 3: config.yaml 生成
|
||
# -------------------------------------------------------
|
||
echo "[3/5] 設定ファイル (config.yaml) の作成..."
|
||
|
||
DEFAULT_OLLAMA_URL="http://localhost:11434/v1"
|
||
DEFAULT_MODEL_NAME="qwen3:8b"
|
||
DEFAULT_ALLOWED_USER="*"
|
||
DEFAULT_MODE="local"
|
||
|
||
if [ -f "config.yaml" ]; then
|
||
EXISTING_MODE=$(grep -E '^\s*integration_mode:' config.yaml | head -1 | sed 's/.*integration_mode:\s*//' | tr -d '"' | tr -d "'" || true)
|
||
EXISTING_OLLAMA_URL=$(grep -E '^\s*base_url:' config.yaml | head -1 | sed 's/.*base_url:\s*//' | tr -d '"' | tr -d "'" || true)
|
||
EXISTING_MODEL_NAME=$(grep -E '^\s*model:' config.yaml | head -1 | sed 's/.*model:\s*//' | tr -d '"' | tr -d "'" || true)
|
||
EXISTING_ALLOWED_USER=$(grep -E '^\s*allowed_users:' config.yaml | head -1 | sed 's/.*\["\?//' | sed 's/"\?\].*//' || true)
|
||
|
||
[ -n "${EXISTING_MODE}" ] && DEFAULT_MODE="${EXISTING_MODE}"
|
||
[ -n "${EXISTING_OLLAMA_URL}" ] && DEFAULT_OLLAMA_URL="${EXISTING_OLLAMA_URL}"
|
||
[ -n "${EXISTING_MODEL_NAME}" ] && DEFAULT_MODEL_NAME="${EXISTING_MODEL_NAME}"
|
||
[ -n "${EXISTING_ALLOWED_USER}" ] && DEFAULT_ALLOWED_USER="${EXISTING_ALLOWED_USER}"
|
||
fi
|
||
|
||
echo ""
|
||
read -rp " 起動モード (local / hybrid / gitea) [${DEFAULT_MODE}]: " INTEGRATION_MODE
|
||
read -rp " Ollama の URL (例: http://gpu-server:11434/v1) [${DEFAULT_OLLAMA_URL}]: " OLLAMA_URL
|
||
read -rp " 使用する LLM モデル名 (例: qwen3:8b) [${DEFAULT_MODEL_NAME}]: " MODEL_NAME
|
||
read -rp " Local/Gitea で受け付けるユーザー名 [${DEFAULT_ALLOWED_USER}]: " ALLOWED_USER
|
||
|
||
INTEGRATION_MODE="${INTEGRATION_MODE:-$DEFAULT_MODE}"
|
||
OLLAMA_URL="${OLLAMA_URL:-$DEFAULT_OLLAMA_URL}"
|
||
MODEL_NAME="${MODEL_NAME:-$DEFAULT_MODEL_NAME}"
|
||
ALLOWED_USER="${ALLOWED_USER:-$DEFAULT_ALLOWED_USER}"
|
||
|
||
GITEA_URL=""
|
||
if [ "$INTEGRATION_MODE" = "hybrid" ] || [ "$INTEGRATION_MODE" = "gitea" ]; then
|
||
EXISTING_GITEA_URL=$(grep -E '^\s*url:' config.yaml 2>/dev/null | head -1 | sed 's/.*url:\s*//' | tr -d '"' | tr -d "'" || true)
|
||
DEFAULT_GITEA_URL="${EXISTING_GITEA_URL:-https://your-gitea.example.com}"
|
||
read -rp " Gitea の URL (例: https://gitea.example.com) [${DEFAULT_GITEA_URL}]: " GITEA_URL
|
||
GITEA_URL="${GITEA_URL:-$DEFAULT_GITEA_URL}"
|
||
fi
|
||
|
||
if [ -f "config.yaml" ]; then
|
||
cp config.yaml config.yaml.bak
|
||
echo " 既存の config.yaml を config.yaml.bak にバックアップしました"
|
||
fi
|
||
|
||
if [ ! -f "config.yaml.example" ]; then
|
||
echo " ERROR: config.yaml.example が見つかりません"
|
||
exit 1
|
||
fi
|
||
|
||
cp config.yaml.example config.yaml
|
||
sed -i "s|^\(\s*integration_mode:\s*\).*|\1${INTEGRATION_MODE}|" config.yaml
|
||
sed -i "s|^\(\s*model:\s*\).*|\1${MODEL_NAME}|" config.yaml
|
||
sed -i "s|^\(\s*base_url:\s*\).*|\1${OLLAMA_URL}|" config.yaml
|
||
sed -i "s|^\(allowed_users:\s*\).*|\1[\"${ALLOWED_USER}\"]|" config.yaml
|
||
if [ -n "${GITEA_URL}" ]; then
|
||
python3 - <<PY
|
||
from pathlib import Path
|
||
path = Path("config.yaml")
|
||
text = path.read_text()
|
||
needle = "# gitea:\n# url: https://your-gitea.example.com\n# token_env: GITEA_API_TOKEN\n# webhook_secret_env: GITEA_WEBHOOK_SECRET\n"
|
||
replacement = "gitea:\n url: ${GITEA_URL}\n token_env: GITEA_API_TOKEN\n webhook_secret_env: GITEA_WEBHOOK_SECRET\n"
|
||
path.write_text(text.replace(needle, replacement))
|
||
PY
|
||
fi
|
||
|
||
python3 - <<'PY'
|
||
from pathlib import Path
|
||
p = Path("config.yaml")
|
||
text = p.read_text()
|
||
if "\nserver:" not in text:
|
||
text += "\nserver:\n tls:\n enabled: true\n"
|
||
p.write_text(text)
|
||
PY
|
||
|
||
echo " config.yaml を生成/更新しました"
|
||
echo ""
|
||
|
||
echo " DB / workspace ディレクトリを準備しています..."
|
||
DB_PATH=$(grep -E '^\s*db_path:' config.yaml | head -1 | sed 's/.*db_path:\s*//' | tr -d '"' | tr -d "'" || true)
|
||
WORKTREE_DIR=$(grep -E '^\s*worktree_dir:' config.yaml | head -1 | sed 's/.*worktree_dir:\s*//' | tr -d '"' | tr -d "'" || true)
|
||
|
||
if [ -n "${DB_PATH}" ]; then
|
||
DB_DIR=$(dirname "${DB_PATH}")
|
||
mkdir -p "${DB_DIR}"
|
||
echo " DB directory: ${DB_DIR}"
|
||
else
|
||
echo " db_path は未設定(既定の data/maestro.db が使われます)"
|
||
mkdir -p data
|
||
echo " fallback DB directory: $(pwd)/data"
|
||
fi
|
||
|
||
if [ -n "${WORKTREE_DIR}" ]; then
|
||
mkdir -p "${WORKTREE_DIR}"
|
||
echo " worktree_dir: ${WORKTREE_DIR}"
|
||
fi
|
||
echo ""
|
||
|
||
# -------------------------------------------------------
|
||
# Step 4: 環境変数の確認
|
||
# -------------------------------------------------------
|
||
echo "[4/5] 環境変数の確認..."
|
||
|
||
if [ "$INTEGRATION_MODE" = "hybrid" ] || [ "$INTEGRATION_MODE" = "gitea" ]; then
|
||
if [ -z "${GITEA_API_TOKEN:-}" ]; then
|
||
echo ""
|
||
echo " GITEA_API_TOKEN が未設定です。"
|
||
echo " Gitea の設定 → アプリケーション → トークン生成 で作成してください。"
|
||
read -rp " Gitea API トークンを入力: " GITEA_API_TOKEN
|
||
export GITEA_API_TOKEN
|
||
fi
|
||
|
||
if [ -z "${GITEA_WEBHOOK_SECRET:-}" ]; then
|
||
# ランダム生成
|
||
GITEA_WEBHOOK_SECRET=$(head -c 32 /dev/urandom | base64 | tr -dc 'a-zA-Z0-9' | head -c 32)
|
||
export GITEA_WEBHOOK_SECRET
|
||
echo " Webhook シークレットを自動生成しました(値は .env に保存します)"
|
||
fi
|
||
|
||
# 認証情報は端末/CI ログに出さない。0600 のファイルへ書き出し、source 指示だけ表示する。
|
||
# 書き出し先は .gitignore 済みの .env(誤コミット防止)。
|
||
ENV_FILE=".env"
|
||
UMASK_PREV=$(umask)
|
||
umask 077
|
||
# 単一クオートで書き出す(値に " や $ や \ が混ざっても .env が壊れないように)。
|
||
# 値中の ' は '\'' にエスケープする。
|
||
shell_quote() { printf "'%s'" "$(printf '%s' "$1" | sed "s/'/'\\\\''/g")"; }
|
||
{
|
||
printf 'export GITEA_API_TOKEN=%s\n' "$(shell_quote "${GITEA_API_TOKEN}")"
|
||
printf 'export GITEA_WEBHOOK_SECRET=%s\n' "$(shell_quote "${GITEA_WEBHOOK_SECRET}")"
|
||
} > "${ENV_FILE}"
|
||
umask "${UMASK_PREV}"
|
||
chmod 600 "${ENV_FILE}"
|
||
|
||
echo ""
|
||
echo " 認証情報を ${ENV_FILE} (権限 0600) に書き出しました。"
|
||
echo " scripts/server.sh は起動時に ${ENV_FILE} を自動で読み込みます。"
|
||
echo " 手動起動 (npm start 等) の場合のみ、事前に読み込んでください:"
|
||
echo ""
|
||
echo " source ${ENV_FILE}"
|
||
echo ""
|
||
else
|
||
echo " local モードのため Gitea 用環境変数は不要です。"
|
||
echo ""
|
||
fi
|
||
|
||
# -------------------------------------------------------
|
||
# Step 5: Webhook 登録
|
||
# -------------------------------------------------------
|
||
echo "[5/5] Webhook の登録..."
|
||
echo ""
|
||
if [ "$INTEGRATION_MODE" = "local" ]; then
|
||
echo " local モードのため Webhook 登録はスキップします。"
|
||
TARGET_REPO=""
|
||
else
|
||
read -rp " 監視するリポジトリ (例: myorg/myrepo、スキップは空Enter): " TARGET_REPO
|
||
fi
|
||
|
||
if [ -n "$TARGET_REPO" ]; then
|
||
read -rp " Orchestrator の外部URL (例: http://10.0.0.10:9876): " ORCH_URL
|
||
ORCH_URL="${ORCH_URL:-http://localhost:9876}"
|
||
|
||
# UI の repo プルダウンに出すため ui_repos へ登録
|
||
if grep -q '^ui_repos:' config.yaml; then
|
||
if grep -q "- ${TARGET_REPO}" config.yaml; then
|
||
echo " ui_repos に ${TARGET_REPO} は既に登録済みです"
|
||
else
|
||
echo " NOTE: ui_repos セクションは既存のため自動追記はスキップしました。"
|
||
echo " config.yaml の ui_repos に '${TARGET_REPO}' を追加してください。"
|
||
fi
|
||
else
|
||
cat >> config.yaml <<YAML
|
||
|
||
ui_repos:
|
||
- ${TARGET_REPO}
|
||
YAML
|
||
echo " ui_repos に ${TARGET_REPO} を追加しました"
|
||
fi
|
||
|
||
GITEA_URL=$(grep -E '^\s*url:' config.yaml | head -1 | sed 's/.*url:\s*//' | tr -d '"' | tr -d "'")
|
||
|
||
WEBHOOK_URL="${ORCH_URL}/webhook"
|
||
|
||
echo ""
|
||
echo " Webhook を登録中..."
|
||
echo " Gitea: ${GITEA_URL}"
|
||
echo " Repository: ${TARGET_REPO}"
|
||
echo " Webhook URL: ${WEBHOOK_URL}"
|
||
|
||
RESPONSE=$(curl -sf -X POST \
|
||
-H "Authorization: token ${GITEA_API_TOKEN}" \
|
||
-H "Content-Type: application/json" \
|
||
"${GITEA_URL}/api/v1/repos/${TARGET_REPO}/hooks" \
|
||
-d "{
|
||
\"type\": \"gitea\",
|
||
\"active\": true,
|
||
\"events\": [\"issues\", \"issue_comment\"],
|
||
\"config\": {
|
||
\"url\": \"${WEBHOOK_URL}\",
|
||
\"content_type\": \"json\",
|
||
\"secret\": \"${GITEA_WEBHOOK_SECRET}\"
|
||
}
|
||
}" 2>&1) || true
|
||
|
||
if echo "$RESPONSE" | python3 -c "import sys,json; print(' Webhook ID:', json.load(sys.stdin)['id'])" 2>/dev/null; then
|
||
echo " 登録完了"
|
||
else
|
||
echo " WARN: Webhook の登録に失敗しました。手動で設定してください。"
|
||
echo " エラー: ${RESPONSE}"
|
||
fi
|
||
else
|
||
echo " スキップ。後から ./scripts/setup-repo.sh で追加できます。"
|
||
fi
|
||
|
||
echo ""
|
||
echo "=========================================="
|
||
echo " セットアップ完了!"
|
||
echo "=========================================="
|
||
echo ""
|
||
echo "起動コマンド:"
|
||
echo ""
|
||
if [ "$INTEGRATION_MODE" = "hybrid" ] || [ "$INTEGRATION_MODE" = "gitea" ]; then
|
||
echo " source .env # GITEA_API_TOKEN / GITEA_WEBHOOK_SECRET を読み込む"
|
||
fi
|
||
echo " npm start"
|
||
echo ""
|
||
echo "管理ダッシュボード: http://localhost:9876/ui/"
|
||
echo ""
|
||
if [ "$INTEGRATION_MODE" != "local" ]; then
|
||
echo "追加リポジトリの監視:"
|
||
echo " ./scripts/setup-repo.sh owner/repo http://this-machine:9876"
|
||
fi
|
||
echo ""
|